All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.exist.client.ClientFrame Maven / Gradle / Ivy

/*
 * eXist Open Source Native XML Database
 * Copyright (C) 2001-2014 The eXist-db Project
 * http://exist-db.org
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *  
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 * 
 */
package org.exist.client;

import org.exist.SystemProperties;
import org.exist.backup.Backup;
import org.exist.backup.CreateBackupDialog;
import org.exist.backup.GuiRestoreServiceTaskListener;
import org.exist.client.security.EditPropertiesDialog;
import org.exist.client.security.UserManagerDialog;
import org.exist.security.*;
import org.exist.security.SecurityManager;
import org.exist.security.internal.aider.PermissionAider;
import org.exist.security.internal.aider.PermissionAiderFactory;
import org.exist.storage.serializers.EXistOutputKeys;
import org.exist.util.FileUtils;
import org.exist.util.MimeTable;
import org.exist.util.SystemExitCodes;
import org.exist.util.crypto.digest.DigestType;
import org.exist.util.crypto.digest.MessageDigest;
import org.exist.util.serializer.SAXSerializer;
import org.exist.util.serializer.SerializerPool;
import org.exist.xmldb.*;
import org.exist.xquery.Constants;
import org.exist.xquery.util.URIUtils;
import org.xml.sax.SAXException;
import org.xmldb.api.DatabaseManager;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.filechooser.FileFilter;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableRowSorter;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.xml.transform.OutputKeys;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;

import static java.nio.charset.StandardCharsets.UTF_8;

/**
 * Main frame of the eXist GUI
 */
public class ClientFrame extends JFrame implements WindowFocusListener, KeyListener, ActionListener, MouseListener {

    private static final long serialVersionUID = 1L;

    public final static String CUT = Messages.getString("ClientFrame.0"); //$NON-NLS-1$
    public final static String COPY = Messages.getString("ClientFrame.1"); //$NON-NLS-1$
    public final static String PASTE = Messages.getString("ClientFrame.2"); //$NON-NLS-1$

    public final static int MAX_DISPLAY_LENGTH = 512000;
    public final static int MAX_HISTORY = 50;

    private final static SimpleAttributeSet promptAttrs = new SimpleAttributeSet();
    private final static SimpleAttributeSet defaultAttrs = new SimpleAttributeSet();
    static {
        StyleConstants.setForeground(promptAttrs, Color.blue);
        StyleConstants.setBold(promptAttrs, true);
        StyleConstants.setForeground(defaultAttrs, Color.black);
    }

    private int commandStart = 0;

    private boolean gotUp = false;
    private DefaultStyledDocument doc;
    private JLabel statusbar;
    private JTable fileman;
    private final ResourceTableModel resources = new ResourceTableModel();
    private JTextPane shell;
    private JPopupMenu shellPopup;
    private final ProcessRunnable processRunnable;
    private final Thread processThread;
    private Preferences preferences;

    private XmldbURI path = null;
    private Properties properties;
    private final InteractiveClient client;

    /**
     * Constructor.
     *
     * @param client Existdb client
     * @param path Database connection URL.
     * @param properties Configuration items.
     * @throws java.awt.HeadlessException Environment  does not support a keyboard, display, or mouse.
     */
    public ClientFrame(final InteractiveClient client, final XmldbURI path, final Properties properties) throws HeadlessException {
        super(Messages.getString("ClientFrame.3")); //$NON-NLS-1$
        this.path = path;
        this.properties = properties;
        this.client = client;
        this.processRunnable = new ProcessRunnable();
        this.processThread = client.newClientThread("process", processRunnable);

        this.setIconImage(InteractiveClient.getExistIcon(getClass()).getImage());

        setupComponents();
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(final WindowEvent ev) {
                close();
            }
        });
        pack();

        processThread.start();

        shell.requestFocus();

        preferences = Preferences.userNodeForPackage(ClientFrame.class);
    }

    private void setupComponents() {
        setJMenuBar(createMenuBar());

        // create the toolbar
        final JToolBar toolbar = new JToolBar();
        URL url = getClass().getResource("icons/Up24.gif"); //$NON-NLS-1$
        JButton button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.5")); //$NON-NLS-1$
        button.addActionListener(this::goUpAction);
        toolbar.add(button);

        url = getClass().getResource("icons/Refresh24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.7")); //$NON-NLS-1$
        button.addActionListener(e -> {
            try {
                client.reloadCollection();
            } catch (final XMLDBException e1) {
                //TODO report message
            }
        });
        toolbar.add(button);
        toolbar.addSeparator();

        url = getClass().getResource("icons/New24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.9")); //$NON-NLS-1$
        button.addActionListener(this::newCollectionAction);
        toolbar.add(button);

        url = getClass().getResource("icons/Add24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.11")); //$NON-NLS-1$
        button.addActionListener(this::uploadAction);
        toolbar.add(button);

        url = getClass().getResource("icons/Delete24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.13")); //$NON-NLS-1$
        button.addActionListener(this::removeAction);
        toolbar.add(button);

        url = getClass().getResource(Messages.getString("ClientFrame.14")); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.15")); //$NON-NLS-1$
        button.addActionListener(e -> {
            try {
                setPermAction(e);
            } catch (final PermissionDeniedException pde) {
                showErrorMessage(pde.getMessage(), pde);
            }
        });
        toolbar.add(button);

        toolbar.addSeparator();
        url = getClass().getResource("icons/Export24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.17")); //$NON-NLS-1$
        button.addActionListener(this::backupAction);
        toolbar.add(button);

        url = getClass().getResource("icons/Import24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.19")); //$NON-NLS-1$
        button.addActionListener(this::restoreAction);
        toolbar.add(button);

        toolbar.addSeparator();
        url = getClass().getResource(Messages.getString("ClientFrame.20")); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.21")); //$NON-NLS-1$
        button.addActionListener(this::editUsersAction);
        toolbar.add(button);

        url = getClass().getResource("icons/Find24.gif"); //$NON-NLS-1$
        button = new JButton(new ImageIcon(url));
        button.setToolTipText(Messages.getString("ClientFrame.23")); //$NON-NLS-1$
        button.addActionListener(this::findAction);
        toolbar.add(button);

        // the split pane separates the resource view table from the shell
        final JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        split.setResizeWeight(0.5);

        // create table for resources and collections
        fileman = new JTable();
        fileman.setModel(resources);
        fileman.setRowSorter(new TableRowSorter(resources));
        fileman.addMouseListener(new TableMouseListener());
        //fileman.setTransferHandler(new TransferHandler(){  
        //});

        fileman.setDropMode(DropMode.ON);
        final DropTarget filemanDropTarget = new DropTarget(fileman, DnDConstants.ACTION_COPY, new FileListDropTargetListener());
        fileman.setDropTarget(filemanDropTarget);

        fileman.setDefaultRenderer(Object.class, new HighlightedTableCellRenderer());
        JScrollPane scroll = new JScrollPane(fileman);
        scroll.setMinimumSize(new Dimension(300, 150));
        split.setLeftComponent(scroll);

        shellPopup = new JPopupMenu(Messages.getString("ClientFrame.24")); //$NON-NLS-1$
        shellPopup.add(new JMenuItem(CUT)).addActionListener(this);
        shellPopup.add(new JMenuItem(COPY)).addActionListener(this);
        shellPopup.add(new JMenuItem(PASTE)).addActionListener(this);

        // shell window
        doc = new DefaultStyledDocument();
        shell = new JTextPane(doc);
        shell.setContentType("text/plain; charset=UTF-8"); //$NON-NLS-1$
        shell.setFont(new Font("Monospaced", Font.PLAIN, 12)); //$NON-NLS-1$
        shell.setMargin(new Insets(7, 5, 7, 5));
        shell.addKeyListener(this);
        shell.addMouseListener(this);

        scroll = new JScrollPane(shell);

        split.setRightComponent(scroll);

        statusbar = new JLabel(Messages.getString("ClientFrame.27") + properties.getProperty(InteractiveClient.USER) + "@" + properties.getProperty(InteractiveClient.URI)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
        statusbar.setMinimumSize(new Dimension(400, 15));
        statusbar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));

        getContentPane().add(split, BorderLayout.CENTER);
        getContentPane().add(toolbar, BorderLayout.NORTH);
        getContentPane().add(statusbar, BorderLayout.SOUTH);
    }

    private JMenuBar createMenuBar() {
        final JMenuBar menubar = new JMenuBar();

        final JMenu fileMenu = new JMenu(Messages.getString("ClientFrame.31")); //$NON-NLS-1$
        fileMenu.setMnemonic(KeyEvent.VK_F);
        menubar.add(fileMenu);

        JMenuItem item = new JMenuItem(Messages.getString("ClientFrame.32"), KeyEvent.VK_S); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::uploadAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.34"), KeyEvent.VK_N); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::newCollectionAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.36"), KeyEvent.VK_B); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::newBlankDocument);
        fileMenu.add(item);
        fileMenu.addSeparator();

        item = new JMenuItem(Messages.getString("ClientFrame.40")); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::removeAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.42"), KeyEvent.VK_C); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::copyAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.44"), KeyEvent.VK_M); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::moveAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.46"), KeyEvent.VK_R); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::renameAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.47"), KeyEvent.VK_E);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::exportAction);
        fileMenu.add(item);

        fileMenu.addSeparator();

        item = new JMenuItem(Messages.getString("ClientFrame.48"), KeyEvent.VK_I); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::reindexAction);
        fileMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.50")); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(e -> {
            try {
                setPermAction(e);
            } catch (final PermissionDeniedException pde) {
                showErrorMessage(pde.getMessage(), pde);
            }
        });
        fileMenu.add(item);

        fileMenu.addSeparator();
        item = new JMenuItem(Messages.getString("ClientFrame.52"), KeyEvent.VK_Q); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(e -> close());
        fileMenu.add(item);

        final JMenu toolsMenu = new JMenu(Messages.getString("ClientFrame.54")); //$NON-NLS-1$
        toolsMenu.setMnemonic(KeyEvent.VK_T);
        menubar.add(toolsMenu);

        item = new JMenuItem(Messages.getString("ClientFrame.55"), KeyEvent.VK_F); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::findAction);
        toolsMenu.add(item);

        toolsMenu.addSeparator();

        item = new JMenuItem(Messages.getString("ClientFrame.57"), KeyEvent.VK_U); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_U,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::editUsersAction);
        toolsMenu.add(item);

        // Disable "Edit Indexes" menu item.
//        item = new JMenuItem(Messages.getString("ClientFrame.59"), KeyEvent.VK_I); //$NON-NLS-1$
//        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_I,
//        		Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
//        item.addActionListener(new ActionListener() {
//            public void actionPerformed(ActionEvent e) {
//                editIndexesAction(e);
//            }
//        });
//        toolsMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.60"), KeyEvent.VK_T);
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
        item.addActionListener(this::editTriggersAction);
        toolsMenu.add(item);

        toolsMenu.addSeparator();

        item = new JMenuItem(Messages.getString("ClientFrame.62a"));
        item.addActionListener(e -> {
            try {
                final DatabaseInstanceManager service = (DatabaseInstanceManager) client.current.getService("DatabaseInstanceManager", "1.0");
                service.enterServiceMode();
            } catch (final XMLDBException ex) {
                showErrorMessage(ex.getMessage(), ex);
            }
        });
        toolsMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.62b"));
        item.addActionListener(e -> {
            try {
                final DatabaseInstanceManager service = (DatabaseInstanceManager) client.current.getService("DatabaseInstanceManager", "1.0");
                service.exitServiceMode();
            } catch (final XMLDBException ex) {
                showErrorMessage(ex.getMessage(), ex);
            }
        });
        toolsMenu.add(item);

        toolsMenu.addSeparator();

        item = new JMenuItem(Messages.getString("ClientFrame.63"), KeyEvent.VK_B); //$NON-NLS-1$
        item.addActionListener(this::backupAction);
        toolsMenu.add(item);

        item = new JMenuItem(Messages.getString("ClientFrame.64"), KeyEvent.VK_R); //$NON-NLS-1$
        item.addActionListener(this::restoreAction);
        toolsMenu.add(item);

        final JMenu connectMenu = new JMenu(Messages.getString("ClientFrame.65")); //$NON-NLS-1$
        connectMenu.setMnemonic(KeyEvent.VK_D);
        menubar.add(connectMenu);

        item = new JMenuItem(Messages.getString("ClientFrame.66"), KeyEvent.VK_S); //$NON-NLS-1$
        item.addActionListener(e -> {
            display(Messages.getString("ClientFrame.67")); //$NON-NLS-1$
            processRunnable.setAction("shutdown"); //$NON-NLS-1$
        });
        connectMenu.add(item);

        // Show LoginPanel to Reconnect
        item = new JMenuItem(Messages.getString("ClientFrame.69"), KeyEvent.VK_U); //$NON-NLS-1$
        item.setToolTipText(Messages.getString("ClientFrame.70")); //$NON-NLS-1$
        item.addActionListener(e -> {
            // load properties modified by the login panel
            final Properties loginData = getLoginData(properties);
            reconnectClient(loginData);
        });
        connectMenu.add(item);


        final JMenu optionsMenu = new JMenu(Messages.getString("ClientFrame.80")); //$NON-NLS-1$
        optionsMenu.setMnemonic(KeyEvent.VK_O);
        menubar.add(optionsMenu);

        JCheckBoxMenuItem check = new JCheckBoxMenuItem(Messages.getString("ClientFrame.81"), "yes".equals(properties.getProperty(OutputKeys.INDENT))); //$NON-NLS-1$
        check.addActionListener(e -> {
            properties.setProperty(OutputKeys.INDENT,
                    ((JCheckBoxMenuItem) e.getSource()).isSelected()
                            ? "yes" //$NON-NLS-1$
                            : "no"); //$NON-NLS-1$
            try {
                client.getResources();
            } catch (final XMLDBException e1) {
                //TODO report error
            }
        });
        optionsMenu.add(check);

        check = new JCheckBoxMenuItem(Messages.getString("ClientFrame.85"), "yes".equals(properties.getProperty(EXistOutputKeys.EXPAND_XINCLUDES))); //$NON-NLS-1$
        check.addActionListener(e -> {
            properties.setProperty(EXistOutputKeys.EXPAND_XINCLUDES,
                    ((JCheckBoxMenuItem) e.getSource()).isSelected()
                            ? "yes" //$NON-NLS-1$
                            : "no"); //$NON-NLS-1$
            try {
                client.getResources();
            } catch (final XMLDBException e1) {
                //TODO report error
            }
        });
        optionsMenu.add(check);


        final JMenu HelpMenu = new JMenu(Messages.getString("ClientFrame.89")); //$NON-NLS-1$
        HelpMenu.setMnemonic(KeyEvent.VK_H);
        menubar.add(HelpMenu);

        item = new JMenuItem(Messages.getString("ClientFrame.90"), KeyEvent.VK_A); //$NON-NLS-1$
        item.addActionListener(e -> AboutAction());
        HelpMenu.add(item);

        return menubar;
    }

    public void reconnectClient(final Properties loginData) {
        if (loginData == null || loginData.isEmpty()) {
            return;
        }

        // make a backup of the current properties
        final Properties oldProps = new Properties();
        oldProps.putAll(properties);

        // update the properties with the new login data
        properties.putAll(loginData);
        statusbar.setText(Messages.getString("ClientFrame.71") + properties.getProperty(InteractiveClient.USER) + "@" + properties.getProperty(InteractiveClient.URI)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$

        try {
            client.shutdown(false);
            client.connect();
            client.reloadCollection();
        } catch (final Exception u) {
            showErrorMessage(Messages.getString("ClientFrame.75") + properties.getProperty(InteractiveClient.URI) + Messages.getString("ClientFrame.77"), u); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

            // restore the previous properties
            properties = oldProps;
            try {
                // attempt to re-connect with previous login data
                client.connect();
            } catch (final Exception uu) {
                showErrorMessage(Messages.getString("ClientFrame.78") + properties.getProperty(InteractiveClient.URI), uu); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
    }

    public void setPath(final XmldbURI currentPath) {
        path = currentPath;
    }

    protected void displayPrompt() {
        final String pathString = path.getCollectionPath();
        try {
            commandStart = doc.getLength();
            doc.insertString(commandStart, Messages.getString("ClientFrame.91"), promptAttrs); //$NON-NLS-1$
            commandStart += 6;
            doc.insertString(commandStart, pathString + '>', promptAttrs);
            commandStart += pathString.length() + 1;
            doc.insertString(commandStart++, Messages.getString("ClientFrame.92"), defaultAttrs); //$NON-NLS-1$
            shell.setCaretPosition(commandStart);
        } catch (final BadLocationException e) {
            //TODO show error
        }
    }

    protected void display(final String message) {
        try {
            commandStart = doc.getLength();
            if (commandStart > MAX_DISPLAY_LENGTH) {
                doc.remove(0, MAX_DISPLAY_LENGTH);
                commandStart = doc.getLength();
            }
            doc.insertString(commandStart, message, defaultAttrs);
            commandStart = doc.getLength();
            shell.setCaretPosition(commandStart);

        } catch (final BadLocationException e) {
            //TODO show error
        }
    }

    protected void setResources(final List rows) {
        resources.setData(rows);
    }

    protected void setStatus(final String message) {
        statusbar.setText(message);
    }

    protected void setEditable(final boolean enabled) {
        shell.setEditable(enabled);
        shell.setVisible(enabled);
    }

    /**
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
     */
    @Override
    public void keyPressed(final KeyEvent e) {
        type(e);
        gotUp = false;
    }

    /**
     * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
     */
    @Override
    public void keyReleased(final KeyEvent e) {
        gotUp = true;
        type(e);
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
     */
    @Override
    public void keyTyped(final KeyEvent e) {
        type(e);
    }

    private synchronized void type(final KeyEvent e) {
        switch (e.getKeyCode()) {
            case KeyEvent.VK_ENTER:
                if (e.getID() == KeyEvent.KEY_PRESSED && gotUp) {
                    enter();
                }
                e.consume();
                break;
            case KeyEvent.VK_HOME:
                shell.setCaretPosition(commandStart);
                e.consume();
                break;
            case KeyEvent.VK_LEFT:
            case KeyEvent.VK_DELETE:
            case KeyEvent.VK_BACK_SPACE:
                if (shell.getCaretPosition() <= commandStart) {
                    e.consume();
                }
                break;
            case KeyEvent.VK_UP:
                if (e.getID() == KeyEvent.KEY_PRESSED) {
                    historyBack();
                }
                e.consume();
                break;
            case KeyEvent.VK_DOWN:
                if (e.getID() == KeyEvent.KEY_PRESSED) {
                    historyForward();
                }
                e.consume();
                break;
            default:
                if ((e.getModifiers() & (InputEvent.CTRL_MASK
                        | InputEvent.META_MASK | InputEvent.ALT_MASK)) == 0) {
                    if (shell.getCaretPosition() < commandStart) {
                        shell.setCaretPosition(doc.getLength());
                    }
                }
                if (e.paramString().indexOf(Messages.getString("ClientFrame.93")) > Constants.STRING_NOT_FOUND) { //$NON-NLS-1$
                    if (shell.getCaretPosition() <= commandStart) {
                        e.consume();
                    }
                }
                break;
        }
    }

    /**
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
     */
    @Override
    public void actionPerformed(final ActionEvent e) {
        final String cmd = e.getActionCommand();
        if (cmd.equals(CUT)) {
            shell.cut();
        } else if (cmd.equals(COPY)) {
            shell.copy();
        } else if (cmd.equals(PASTE)) {
            shell.paste();
        }
    }

    private void newBlankDocument(final ActionEvent e) {
        final JFrame dialog = new NewResourceDialog(client);
        dialog.setVisible(true);
    }


    private void goUpAction(final ActionEvent ev) {
        display(Messages.getString("ClientFrame.94")); //$NON-NLS-1$
        processRunnable.setAction("cd .."); //$NON-NLS-1$
    }

    private void newCollectionAction(final ActionEvent ev) {
        final String newCol = JOptionPane.showInputDialog(this, Messages.getString("ClientFrame.96")); //$NON-NLS-1$
        if (newCol != null) {
            final String command = "mkcol \"" + newCol + '"'; //$NON-NLS-1$
            display(command + "\n"); //$NON-NLS-1$
            processRunnable.setAction(command);
        }
    }

    /**
     * Returns an array of user-selected resources.
     */
    private ResourceDescriptor[] getSelectedResources() {
        final int[] selectedRows = fileman.getSelectedRows();
        final ResourceDescriptor[] res = new ResourceDescriptor[selectedRows.length];

        for (int i = 0; i < selectedRows.length; i++) {
            res[i] = resources.getRow(fileman.convertRowIndexToModel(selectedRows[i]));
        }

        return res;
    }

    private void removeAction(final ActionEvent ev) {

        final ResourceDescriptor[] res = getSelectedResources();
        final Collection removeRootCollection = client.current;
        // String cmd;
        if (JOptionPane.showConfirmDialog(this,
                Messages.getString("ClientFrame.104") + Messages.getString("ClientFrame.105"), //$NON-NLS-1$ //$NON-NLS-2$
                Messages.getString("ClientFrame.106"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { //$NON-NLS-1$
            final Runnable removeTask = () -> {
                final ProgressMonitor monitor = new ProgressMonitor(ClientFrame.this, Messages.getString("ClientFrame.107"), Messages.getString("ClientFrame.108"), 1, res.length); //$NON-NLS-1$ //$NON-NLS-2$
                monitor.setMillisToDecideToPopup(500);
                monitor.setMillisToPopup(500);
                for (int i = 0; i < res.length; i++) {
                    final ResourceDescriptor resource = res[i];
                    if (resource.isCollection()) {
                        try {
                            final EXistCollectionManagementService mgtService = (EXistCollectionManagementService) removeRootCollection
                                    .getService(
                                            "CollectionManagementService", //$NON-NLS-1$
                                            "1.0"); //$NON-NLS-1$
                            mgtService
                                    .removeCollection(resource.getName());
                        } catch (final XMLDBException e) {
                            showErrorMessage(e.getMessage(), e);
                        }
                    } else {
                        try {
                            final Resource res1 = removeRootCollection
                                    .getResource(resource.getName().toString());
                            removeRootCollection.removeResource(res1);
                        } catch (final XMLDBException e) {
                            showErrorMessage(e.getMessage(), e);
                        }
                    }
                    monitor.setProgress(i + 1);
                    if (monitor.isCanceled()) {
                        return;
                    }
                }

                try {
                    removeRootCollection.close();
                } catch (final XMLDBException e) {
                    showErrorMessage(e.getMessage(), e);
                }

                try {
                    client.getResources();
                } catch (final XMLDBException e) {
                    showErrorMessage(e.getMessage(), e);
                }
            };
            client.newClientThread("remove", removeTask).start();
        }
    }

    private void moveAction(final ActionEvent ev) {
        final ResourceDescriptor[] res = getSelectedResources();

        PrettyXmldbURI[] collections;

        //get an array of collection paths
        try {
            final Collection root = client.getCollection(XmldbURI.ROOT_COLLECTION);
            final List alCollections = getCollections(root, new ArrayList());
            collections = new PrettyXmldbURI[alCollections.size()];
            alCollections.toArray(collections);
        } catch (final XMLDBException e) {
            showErrorMessage(e.getMessage(), e);
            return;
        }

        //prompt the user for a destination collection from the list
        final Object val = JOptionPane.showInputDialog(this, Messages.getString("ClientFrame.111"), Messages.getString("ClientFrame.112"), JOptionPane.QUESTION_MESSAGE, null, collections, collections[0]); //$NON-NLS-1$ //$NON-NLS-2$
        if (val == null) {
            return;
        }

        final XmldbURI destinationPath = ((PrettyXmldbURI) val).getTargetURI();
        final Runnable moveTask = () -> {
            try {
                final EXistCollectionManagementService service = (EXistCollectionManagementService)
                        client.current.getService("CollectionManagementService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
                for (int i = 0; i < res.length; i++) {
                    setStatus(Messages.getString("ClientFrame.115") + res[i].getName() + Messages.getString("ClientFrame.116") + destinationPath + Messages.getString("ClientFrame.117")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    if (res[i].isCollection()) {
                        service.move(res[i].getName(), destinationPath, null);
                    } else {
                        service.moveResource(res[i].getName(), destinationPath, null);
                    }
                }
                client.reloadCollection();
            } catch (final XMLDBException e) {
                showErrorMessage(e.getMessage(), e);
            }
            setStatus(Messages.getString("ClientFrame.118")); //$NON-NLS-1$
        };
        client.newClientThread("move", moveTask).start();
    }

    private void renameAction(final ActionEvent ev) {
        final ResourceDescriptor[] res = getSelectedResources();
        String inputValue = "";
        try {
            inputValue = res[0].getName().toString();
        } catch (Exception npe) {
        }
        final Object val = JOptionPane.showInputDialog(this, Messages.getString("ClientFrame.119"), Messages.getString("ClientFrame.120"), JOptionPane.QUESTION_MESSAGE, null, null, inputValue); //$NON-NLS-1$ //$NON-NLS-2$

        if (val == null) {
            return;
        }

        XmldbURI parseIt;
        try {
            parseIt = URIUtils.encodeXmldbUriFor((String) val);
        } catch (final URISyntaxException e) {
            showErrorMessage(Messages.getString("ClientFrame.121") + e.getMessage(), e); //$NON-NLS-1$
            return;
        }
        final XmldbURI destinationFilename = parseIt;
        final Runnable renameTask = () -> {
            try {
                final EXistCollectionManagementService service = (EXistCollectionManagementService)
                        client.current.getService("CollectionManagementService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
                for (int i = 0; i < res.length; i++) {
                    setStatus(Messages.getString("ClientFrame.124") + res[i].getName() + Messages.getString("ClientFrame.125") + destinationFilename + Messages.getString("ClientFrame.126")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    if (res[i].isCollection()) {
                        service.move(res[i].getName(), null, destinationFilename);
                    } else {
                        service.moveResource(res[i].getName(), null, destinationFilename);
                    }
                }
                client.reloadCollection();
            } catch (final XMLDBException e) {
                showErrorMessage(e.getMessage(), e);
            }
            setStatus(Messages.getString("ClientFrame.127")); //$NON-NLS-1$
        };
        client.newClientThread("rename", renameTask).start();
    }

    private void copyAction(final ActionEvent ev) {

        final ResourceDescriptor[] res = getSelectedResources();
        PrettyXmldbURI[] collections;

        //get an array of collection paths
        try {
            final Collection root = client.getCollection(XmldbURI.ROOT_COLLECTION);
            final List alCollections = getCollections(root, new ArrayList());
            collections = new PrettyXmldbURI[alCollections.size()];
            alCollections.toArray(collections);
        } catch (final XMLDBException e) {
            showErrorMessage(e.getMessage(), e);
            return;
        }

        //prompt the user for a destination collection from the list
        final Object val = JOptionPane.showInputDialog(this, Messages.getString("ClientFrame.128"), Messages.getString("ClientFrame.129"), JOptionPane.QUESTION_MESSAGE, null, collections, collections[0]); //$NON-NLS-1$ //$NON-NLS-2$
        if (val == null) {
            return;
        }

        final XmldbURI destinationPath = ((PrettyXmldbURI) val).getTargetURI();

        final Runnable moveTask = () -> {
            try {
                final EXistCollectionManagementService service = (EXistCollectionManagementService)
                        client.current.getService("CollectionManagementService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
                for (int i = 0; i < res.length; i++) {

                    //TODO
                    //what happens if the source and destination paths are the same?
                    //we need to check and prompt the user to either skip or choose a new name
                    //this function can copy multiple resources/collections selected by the user,
                    //so may need to prompt the user multiple times? is in this thread the correct
                    //place to do it? also need to do something similar for moveAction()
                    //
                    //Its too late and brain hurts - deliriumsky

                    setStatus(Messages.getString("ClientFrame.132") + res[i].getName() + Messages.getString("ClientFrame.133") + destinationPath + Messages.getString("ClientFrame.134")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                    if (res[i].isCollection()) {
                        service.copy(res[i].getName(), destinationPath, null);
                    } else {
                        service.copyResource(res[i].getName(), destinationPath, null);
                    }
                }
                client.reloadCollection();
            } catch (final XMLDBException e) {
                showErrorMessage(e.getMessage(), e);
            }
            setStatus(Messages.getString("ClientFrame.135")); //$NON-NLS-1$
        };
        client.newClientThread("move", moveTask).start();
    }

    private ArrayList getCollections(final Collection root, final ArrayList collectionsList) throws XMLDBException {
        collectionsList.add(new PrettyXmldbURI(XmldbURI.create(root.getName())));
        final String[] childCollections = root.listChildCollections();
        Collection child = null;
        for (int i = 0; i < childCollections.length; i++) {
            try {
                child = root.getChildCollection(childCollections[i]);
            } catch (final XMLDBException xmldbe) {
                if (xmldbe.getCause() instanceof PermissionDeniedException) {
                    continue;
                } else {
                    throw xmldbe;
                }
            } catch (Exception npe) {
                System.out.println("Corrupted resource/collection skipped: " + child != null ? child.getName() != null ? child.getName() : "unknown" : "unknown");
                continue;
            }
            try {
                getCollections(child, collectionsList);
            } catch (Exception ee) {
                System.out.println("Corrupted resource/collection skipped: " + child != null ? child.getName() != null ? child.getName() : "unknown" : "unknown");
                continue;
            }
        }
        return collectionsList;
    }

    private void reindexAction(final ActionEvent ev) {
        final int[] selRows = fileman.getSelectedRows();
        final ResourceDescriptor[] res;
        if (selRows.length == 0) {
            res = new ResourceDescriptor[1];
            res[0] = new ResourceDescriptor.Collection(client.path);
        } else {
            res = new ResourceDescriptor[selRows.length];

            for (int i = 0; i < selRows.length; i++) {
                res[i] = resources.getRow(fileman.convertRowIndexToModel(selRows[i]));
                if (!(res[i].isCollection())) {
                    JOptionPane.showMessageDialog(this, Messages.getString("ClientFrame.136"), Messages.getString("ClientFrame.137"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
                    return;
                }
            }
        }

        if (JOptionPane.showConfirmDialog(this,
                Messages.getString("ClientFrame.138"), //$NON-NLS-1$
                Messages.getString("ClientFrame.139"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { //$NON-NLS-1$
            final ResourceDescriptor collections[] = res;
            final Runnable reindexThread = () -> {
                ClientFrame.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                final IndexQueryService service;
                try {
                    service = (IndexQueryService)
                            client.current.getService("IndexQueryService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
                    for (int i = 0; i < collections.length; i++) {
                        final ResourceDescriptor next = collections[i];
                        setStatus(Messages.getString("ClientFrame.142") + next.getName() + Messages.getString("ClientFrame.143")); //$NON-NLS-1$ //$NON-NLS-2$
                        service.reindexCollection(next.getName());
                    }
                    setStatus(Messages.getString("ClientFrame.144")); //$NON-NLS-1$
                } catch (final XMLDBException e) {
                    showErrorMessage(e.getMessage(), e);
                }
                ClientFrame.this.setCursor(Cursor.getDefaultCursor());
            };
            client.newClientThread("reindex", reindexThread).start();
        }
    }

    private void uploadAction(final ActionEvent ev) {
        // TODO store last file choose in properties
        final JFileChooser chooser = new JFileChooser(preferences.get("directory.last", System.getProperty("user.dir")));
        chooser.setMultiSelectionEnabled(true);
        chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
        chooser.addChoosableFileFilter(new BinaryFileFilter());
        chooser.addChoosableFileFilter(new XMLFileFilter());
        if (chooser.showDialog(this, Messages.getString("ClientFrame.146")) == JFileChooser.APPROVE_OPTION) { //$NON-NLS-1$
            // remember directory in preferences
            preferences.put("directory.last", chooser.getCurrentDirectory().getAbsolutePath());

            uploadFiles(FileUtils.asPathsList(chooser.getSelectedFiles()));
        }
    }

    private void uploadFiles(final List files) {
        if (files != null && !files.isEmpty()) {
            final Runnable uploadTask = () -> {
                final UploadDialog upload = new UploadDialog();
                try {
                    client.parse(files, upload);
                    client.getResources();
                } catch (final XMLDBException e) {
                    showErrorMessage(Messages.getString("ClientFrame.147") + e.getMessage(), e);
                }
            };
            client.newClientThread("upload", uploadTask).start();
        }
    }

    private boolean deleteDirectory(final Path target) {
        try {
            FileUtils.delete(target);
            return true;
        } catch (final IOException e) {
            return false;
        }

    }

    private void backupAction(final ActionEvent ev) {

        //get the collection to highlight in the backup dialog
        final String defaultSelectedCollection;
        final ResourceDescriptor selResources[] = getSelectedResources();
        if (selResources != null) {
            if (selResources.length == 1 && selResources[0].isCollection()) {
                //use the selected collection
                defaultSelectedCollection = path.toString() + "/" + selResources[0].getName().toString();
            } else {
                //use the current collection
                defaultSelectedCollection = path.toString();
            }
        } else {
            defaultSelectedCollection = path.toString();
        }

        final CreateBackupDialog dialog = new CreateBackupDialog(
                properties.getProperty(InteractiveClient.URI, "xmldb:exist://"),
                properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER),
                properties.getProperty(InteractiveClient.PASSWORD, null),
                Paths.get(preferences.get("directory.backup", System.getProperty("user.home"))),
                defaultSelectedCollection
        );

        if (JOptionPane.showOptionDialog(this, dialog, Messages.getString("ClientFrame.157"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null) == JOptionPane.YES_OPTION) {

            final String collection = dialog.getCollection();
            final String backuptarget = dialog.getBackupTarget();
            final boolean deduplicateBlobs = dialog.getDeduplicateBlobs();

            // DWES add check here?
            final Path target = Paths.get(backuptarget).normalize();
            if (Files.exists(target)) {
                final int response = JOptionPane.showConfirmDialog(this,
                        String.format("%s %s %s", Messages.getString("CreateBackupDialog.6a"), backuptarget, Messages.getString("CreateBackupDialog.6b")),
                        Messages.getString("CreateBackupDialog.6c"), JOptionPane.YES_NO_OPTION);

                if (response == JOptionPane.CLOSED_OPTION) {
                    // User clicked close window
                    return;

                } else if (response == JOptionPane.YES_OPTION) {
                    // User wants directory to be removed
                    deleteDirectory(target);
                }

            }


            try {
                final Backup backup = new Backup(
                        properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER),
                        properties.getProperty(InteractiveClient.PASSWORD, null), Paths.get(backuptarget),
                        XmldbURI.xmldbUriFor(properties.getProperty(InteractiveClient.URI, "xmldb:exist://") + collection),
                        null,
                        deduplicateBlobs
                );
                backup.backup(true, this);
            } catch (final XMLDBException | IOException | SAXException | URISyntaxException e) {
                showErrorMessage(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
            }
        }
    }

    private void restoreAction(final ActionEvent ev) {
        final JFileChooser chooser = new JFileChooser(preferences.get("directory.backup", System.getProperty("user.dir")));
        chooser.setMultiSelectionEnabled(false);
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.addChoosableFileFilter(new BackupContentsFilter());
        // re-enable later
        chooser.addChoosableFileFilter(new ZipFilter());

        if (chooser.showDialog(null, Messages.getString("ClientFrame.169")) == JFileChooser.APPROVE_OPTION) { //$NON-NLS-1$
            final Path f = chooser.getSelectedFile().toPath();
            preferences.put("directory.backup", chooser.getCurrentDirectory().getAbsolutePath());
            final JPanel askPass = new JPanel(new BorderLayout());
            askPass.add(new JLabel(Messages.getString("ClientFrame.170")), BorderLayout.NORTH); //$NON-NLS-1$
            final JPasswordField passInput = new JPasswordField(25);
            final JCheckBox overwriteCb = new JCheckBox(Messages.getString("ClientFrame.227"), false);
            askPass.add(passInput, BorderLayout.CENTER);
            askPass.add(overwriteCb, BorderLayout.SOUTH);
            if (JOptionPane.showOptionDialog(this, askPass, Messages.getString("ClientFrame.171"), //$NON-NLS-1$
                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
                    null, null, null) == JOptionPane.YES_OPTION) {
                final String newDbaPass = passInput.getPassword().length == 0 ? null : new String(passInput.getPassword());
                final String restoreFile = f.toAbsolutePath().toString();
                final boolean overwriteApps = overwriteCb.isSelected();
                final GuiRestoreServiceTaskListener listener = new GuiRestoreServiceTaskListener(this);
                doRestore(listener, properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER), properties.getProperty(InteractiveClient.PASSWORD, null), newDbaPass, Paths.get(restoreFile), properties.getProperty(InteractiveClient.URI, "xmldb:exist://"), overwriteApps);
            }
        }
    }

    private void doRestore(final GuiRestoreServiceTaskListener listener, final String username, final String password,
                           final String dbaPassword, final Path f, final String uri, final boolean overwriteApps) {

        final Runnable restoreTask = () -> {

            try {
                final XmldbURI dbUri;
                if(!uri.endsWith(XmldbURI.ROOT_COLLECTION)) {
                    dbUri = XmldbURI.xmldbUriFor(uri + XmldbURI.ROOT_COLLECTION);
                } else {
                    dbUri = XmldbURI.xmldbUriFor(uri);
                }

                final Collection collection = DatabaseManager.getCollection(dbUri.toString(), username, password);
                final EXistRestoreService service = (EXistRestoreService) collection.getService("RestoreService", "1.0");
                service.restore(f.toAbsolutePath().toString(), dbaPassword, listener, overwriteApps);

                if (JOptionPane.showConfirmDialog(null, Messages.getString("ClientFrame.223"), Messages.getString("ClientFrame.224"),
                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
                    setStatus(Messages.getString("ClientFrame.225"));
                    repairRepository(client.getCollection());
                    setStatus(Messages.getString("ClientFrame.226"));
                }

                listener.hideDialog();

                if (properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER).equals(SecurityManager.DBA_USER) && dbaPassword != null) {
                    properties.setProperty(InteractiveClient.PASSWORD, dbaPassword);
                }

                SwingUtilities.invokeAndWait(() -> {
                    try {
                        client.reloadCollection();
                    } catch (final XMLDBException xe) {
                        xe.printStackTrace();
                    }
                });
            } catch (final Exception e) {
                showErrorMessage(Messages.getString("ClientFrame.181") + e.getMessage(), e); //$NON-NLS-1$
            } finally {
                if (listener.hasProblems()) {
                    showErrorMessage(Messages.getString("ClientFrame.181") + listener.getAllProblems(), null);
                }
            }
        };

        client.newClientThread("restore", restoreTask).start();
    }

    public static void repairRepository(Collection collection) throws XMLDBException {
        final EXistXQueryService service = (EXistXQueryService) collection.getService("XQueryService", "1.0");
        service.query("import module namespace repair=\"http://exist-db.org/xquery/repo/repair\"\n" +
                "at \"resource:org/exist/xquery/modules/expathrepo/repair.xql\";\n" +
                "repair:clean-all(),\n" +
                "repair:repair()");
    }

    public UserManagementService getUserManagementService() throws XMLDBException {
        final Collection collection = client.getCollection();
        return (UserManagementService) collection.getService("UserManagementService", "1.0");
    }

    private void editUsersAction(final ActionEvent ev) {
        try {
            final UserManagementService userManagementService = getUserManagementService();

            final UserManagerDialog userManager = new UserManagerDialog(userManagementService, client.getProperties().getProperty(InteractiveClient.USER), this);
            userManager.setVisible(true);

        } catch (final XMLDBException e) {
            showErrorMessage(Messages.getString("ClientFrame.185"), e); //$NON-NLS-1$
            e.printStackTrace();
        }
    }

    private void exportAction(final ActionEvent ev) {
        if (fileman.getSelectedRowCount() == 0) {
            return;
        }
        final int[] rows = fileman.getSelectedRows();
        for (final int row : rows) {
            final ResourceDescriptor desc = resources.getRow(fileman.convertRowIndexToModel(row));
            if (desc.isCollection()) {
                continue;
            }

            final JFileChooser chooser = new JFileChooser(preferences.get("directory.last", System.getProperty("user.dir")));
            chooser.setMultiSelectionEnabled(false);
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setSelectedFile(Paths.get(desc.getName().getCollectionPath()).toFile());
            if (chooser.showDialog(this, "Select file for export") == JFileChooser.APPROVE_OPTION) {
                preferences.put("directory.last", chooser.getCurrentDirectory().getAbsolutePath());
                final Path file = chooser.getSelectedFile().toPath();
                if (Files.exists(file)
                        && JOptionPane.showConfirmDialog(this,
                        "File exists. Overwrite?", "Overwrite?",
                        JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
                    return;
                }
                final Resource resource;
                final SAXSerializer contentSerializer;
                try {
                    final Collection collection = client.getCollection();
                    resource = collection
                            .getResource(desc.getName().toString());
                    if (resource instanceof ExtendedResource) {
                        try(final OutputStream os = Files.newOutputStream(file)) {
                            ((ExtendedResource) resource).getContentIntoAStream(os);
                        }
                    } else {
                        try(final Writer writer = Files.newBufferedWriter(file, UTF_8)) {
                            // write resource to contentSerializer
                            contentSerializer = (SAXSerializer) SerializerPool
                                    .getInstance()
                                    .borrowObject(SAXSerializer.class);
                            contentSerializer.setOutput(writer, properties);
                            ((EXistResource) resource)
                                    .setLexicalHandler(contentSerializer);
                            ((XMLResource) resource)
                                    .getContentAsSAX(contentSerializer);
                            SerializerPool.getInstance().returnObject(contentSerializer);
                        }
                    }
                    //TODO finally close os
                } catch (final Exception e) {
                    System.err.println("An exception occurred" + e.getMessage());
                    e.printStackTrace();
                }
            }
        }
    }

    private void editIndexesAction(final ActionEvent ev) {
        final IndexDialog dialog = new IndexDialog(Messages.getString("ClientFrame.186"), client); //$NON-NLS-1$
        dialog.setVisible(true);
    }

    private void editTriggersAction(final ActionEvent ev) {
        final TriggersDialog dialog = new TriggersDialog("Edit Triggers", client);
        dialog.setVisible(true);
    }

    private void findAction(final ActionEvent ev) {
        final Collection collection = client.getCollection();
        final QueryDialog dialog = new QueryDialog(client, collection, properties);
        dialog.setVisible(true);
    }

    private void setPermAction(final ActionEvent ev) throws PermissionDeniedException {
        if (fileman.getSelectedRowCount() == 0) {
            return;
        }
        try {
            final Collection collection = client.getCollection();
            final UserManagementService service = getUserManagementService();
            PermissionAider permAider = null;
            XmldbURI name;
            Date created = new Date();
            Date modified = null;
            Long size = null;
            MessageDigest messageDigest = null;
            String mimeType = null;

            if (fileman.getSelectedRowCount() == 1) {
                final int row = fileman.convertRowIndexToModel(fileman.getSelectedRow());
                final ResourceDescriptor desc = resources.getRow(row);
                name = desc.getName();

                final Permission perm;
                if (desc.isCollection()) {
                    final Collection coll = collection.getChildCollection(name.toString());
                    created = ((EXistCollection) coll).getCreationTime();
                    perm = service.getPermissions(coll);
                } else {
                    final Resource res = collection.getResource(name.toString());
                    created = ((EXistResource) res).getCreationTime();
                    modified = ((EXistResource) res).getLastModificationTime();
                    mimeType = ((EXistResource) res).getMimeType();
                    perm = service.getPermissions(res);
                    if (res instanceof EXistBinaryResource) {
                        messageDigest = ((EXistBinaryResource) res).getContentDigest(DigestType.BLAKE_256);
                        size = ((EXistBinaryResource) res).getContentLength();
                    }
                }

                //this is a local instance, we cannot use disconnected local instance in the ResourcePropertyDialog
                permAider = PermissionAiderFactory.getPermission(perm.getOwner().getName(), perm.getGroup().getName(), perm.getMode());
                //copy acl
                if (perm instanceof ACLPermission && permAider instanceof ACLPermission) {
                    final ACLPermission aclPermission = (ACLPermission) perm;
                    for (int i = 0; i < aclPermission.getACECount(); i++) {
                        ((ACLPermission) permAider).addACE(aclPermission.getACEAccessType(i), aclPermission.getACETarget(i), aclPermission.getACEWho(i), aclPermission.getACEMode(i));
                    }
                }

            } else {
                name = XmldbURI.create(".."); //$NON-NLS-1$
                final Account account = service.getAccount(properties.getProperty(InteractiveClient.USER));
                permAider = PermissionAiderFactory.getPermission(account.getName(), account.getPrimaryGroup(), Permission.DEFAULT_RESOURCE_PERM); //$NON-NLS-1$ //$NON-NLS-2$
            }

            final List selected = new ArrayList();
            final int rows[] = fileman.getSelectedRows();
            for (int i = 0; i < rows.length; i++) {
                selected.add(resources.getRow(fileman.convertRowIndexToModel(rows[i])));
            }

            final EditPropertiesDialog editPropertiesDialog = new EditPropertiesDialog(service, client.getProperties().getProperty(InteractiveClient.USER), collection, name, mimeType, created, modified, size, messageDigest, permAider, selected);
            editPropertiesDialog.addWindowListener(new WindowAdapter() {
                @Override
                public void windowClosed(final WindowEvent e) {
                    try {
                        client.reloadCollection();
                    } catch (final XMLDBException xmldbe) {
                        showErrorMessage(Messages.getString("ClientFrame.197") + xmldbe.getMessage(), xmldbe); //$NON-NLS-1$
                        xmldbe.printStackTrace();
                    }
                }
            });
            editPropertiesDialog.setVisible(true);

        } catch (final XMLDBException e) {
            showErrorMessage(Messages.getString("ClientFrame.197") + e.getMessage(), e); //$NON-NLS-1$
            e.printStackTrace();
        }
    }

    private void enter() {
        int end = doc.getLength();
        if (end - commandStart == 0) {
            return;
        }
        try {
            final String command = doc.getText(commandStart, end - commandStart);
            commandStart = end;
            doc.insertString(commandStart++, "\n", defaultAttrs); //$NON-NLS-1$
            if (command != null) {
                processRunnable.setAction(command);
                client.console.getHistory().add(command);
            }
        } catch (final BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void historyBack() {
        client.console.getHistory().previous();
        final String item = client.console.getHistory().current();
        if (item == null) {
            return;
        }
        try {
            if (shell.getCaretPosition() > commandStart) {
                doc.remove(commandStart, doc.getLength() - commandStart);
            }
            doc.insertString(commandStart, item, defaultAttrs);
        } catch (final BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void historyForward() {
        client.console.getHistory().next();
        final String item = client.console.getHistory().current();
        try {
            if (shell.getCaretPosition() > commandStart) {
                doc.remove(commandStart, doc.getLength() - commandStart);
            }
            doc.insertString(commandStart, item, defaultAttrs);
        } catch (final BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void close() {
        setVisible(false);
        dispose();
        processThread.interrupt();
        System.exit(SystemExitCodes.OK_EXIT_CODE);
    }

    private void AboutAction() {
        JOptionPane.showMessageDialog(this, client.getNotice());
    }

    class TableMouseListener extends MouseAdapter {

        @Override
        public void mouseClicked(final MouseEvent e) {
            if (e.getClickCount() == 2) {
                final int row = fileman.convertRowIndexToModel(fileman.getSelectedRow());
                final ResourceDescriptor resource = resources.getRow(row);
                if (resource.isCollection()) {
                    // cd into collection
                    final String command = "cd " + '"' + resource.getName() + '"'; //$NON-NLS-1$
                    display(command + "\n"); //$NON-NLS-1$
                    processRunnable.setAction(command);
                } else {
                    // open a document for editing
                    ClientFrame.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
                    try {
                        final Resource doc = client.retrieve(resource.getName(), properties.getProperty(OutputKeys.INDENT, "yes")); //$NON-NLS-1$

                        if ("application/xquery".equals(((EXistResource) doc).getMimeType())) {
                            final Collection collection = client.getCollection();
                            final QueryDialog dialog = new QueryDialog(client, collection, doc, properties);
                            dialog.setVisible(true);
                        } else {
                            final DocumentView view = new DocumentView(client, resource.getName(), doc, properties);
                            view.setSize(new Dimension(640, 400));
                            view.viewDocument();
                        }
                        //doc will be closed in one of the dialogs above when they are closed
                    } catch (final XMLDBException ex) {
                        showErrorMessage(Messages.getString("ClientFrame.206") + ex.getMessage(), ex); //$NON-NLS-1$
                    }

                    ClientFrame.this.setCursor(Cursor.getDefaultCursor());
                }
            }
        }

    }

    /**
     * Compares resources according to their name, ensuring that collections
     * always are before documents.
     *
     * @author gpothier
     */
    private static class ResourceComparator implements Comparator {
        @Override
        public int compare(final ResourceDescriptor desc1, final ResourceDescriptor desc2) {
            if (desc1.isCollection() != desc2.isCollection()) {
                return desc1.isCollection() ? Constants.INFERIOR : Constants.SUPERIOR;
            } else {
                return desc1.getName().compareTo(desc2.getName());
            }
        }
    }

    private class ProcessRunnable implements Runnable {
        private final TransferQueue queue = new LinkedTransferQueue<>();

        public void setAction(final String action) {
            queue.add(action);
        }

        @Override
        public void run() {
            while (true) {
                final String action;
                try {
                    action = queue.take();
                } catch (final InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }

                final boolean status = client.process(action);
                displayPrompt();

                if (!status) {
                    close();
                    break;
                }
            }
        }
    }

    static class ResourceTableModel extends AbstractTableModel {

        private static final long serialVersionUID = 1L;

        private final String[] columnNames = new String[]{
                Messages.getString("ClientFrame.207") //$NON-NLS-1$
                , Messages.getString("ClientFrame.208") //$NON-NLS-1$
                , Messages.getString("ClientFrame.209") //$NON-NLS-1$
                , Messages.getString("ClientFrame.210") //$NON-NLS-1$
                , Messages.getString("ClientFrame.211") //$NON-NLS-1$
        };

        private List rows = null;

        private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        public void setData(final List rows) {
            Collections.sort(rows, new ResourceComparator());
            this.rows = rows;
            fireTableDataChanged();
        }

        public ResourceDescriptor getRow(final int index) {
            return getRowCount() > 0 ? rows.get(index) : null;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.table.TableModel#getColumnCount()
         */
        @Override
        public int getColumnCount() {
            return columnNames.length;
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.table.TableModel#getColumnName(int)
         */
        @Override
        public String getColumnName(int column) {
            return columnNames[column];
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.table.TableModel#getRowCount()
         */
        @Override
        public int getRowCount() {
            return rows == null ? 0 : rows.size();
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.table.TableModel#getValueAt(int, int)
         */
        @Override
        public Object getValueAt(final int rowIndex, final int columnIndex) {
            if (getRowCount() > 0) {
                final ResourceDescriptor row = getRow(rowIndex);
                switch (columnIndex) {
                    case 0:
                        return row.getName().toString();
                    case 1:
                        return dateFormat.format(row.getDate());
                    case 2:
                        return row.getOwner();
                    case 3:
                        return row.getGroup();
                    case 4:
                        return row.getPermissions();
                    default:
                        throw new RuntimeException(Messages.getString("ClientFrame.212")); //$NON-NLS-1$
                }
            }
            return "";
        }

        /*
         * (non-Javadoc)
         *
         * @see javax.swing.table.TableModel#getColumnClass(int)
         */
        @Override
        public Class getColumnClass(final int column) {
            return getValueAt(0, column).getClass();
        }
    }

    /**
     * @param props pass properties to the login panel
     * @return the modified properties
     */
    protected static Properties getLoginData(final Properties props) {

        final Properties properties = new Properties();

        final String serverUri;
        if (props.getProperty(InteractiveClient.URI) == null || props.getProperty(InteractiveClient.URI).isEmpty()) {
            serverUri = InteractiveClient.URI_DEFAULT;
        } else {
            if (Boolean.parseBoolean(props.getProperty(InteractiveClient.LOCAL_MODE, "FALSE"))) {
                serverUri = InteractiveClient.URI_DEFAULT;
            } else {
                serverUri = props.getProperty(InteractiveClient.URI);
            }
        }

        final DefaultConnectionSettings defaultConnectionSettings = new DefaultConnectionSettings(
                props.getProperty(InteractiveClient.USER, InteractiveClient.USER_DEFAULT),
                props.getProperty(InteractiveClient.PASSWORD, ""),
                serverUri,
                Boolean.parseBoolean(props.getProperty(InteractiveClient.SSL_ENABLE, InteractiveClient.SSL_ENABLE_DEFAULT))
        );
        defaultConnectionSettings.setConfiguration(props.getProperty(InteractiveClient.CONFIGURATION, ""));

        final ConnectionDialog connectionDialog = new ConnectionDialog(null, true, defaultConnectionSettings, Boolean.parseBoolean(props.getProperty(InteractiveClient.LOCAL_MODE, InteractiveClient.LOCAL_MODE_DEFAULT)), Boolean.parseBoolean(props.getProperty(InteractiveClient.NO_EMBED_MODE, InteractiveClient.NO_EMBED_MODE_DEFAULT)));

        connectionDialog.setTitle(SystemProperties.getInstance().getSystemProperty("product-name", "eXist-db") + " " + SystemProperties.getInstance().getSystemProperty("product-version", "unknown") + " Database Login");

        connectionDialog.addDialogCompleteWithResponseCallback(connection -> {
            properties.setProperty(InteractiveClient.USER, connection.getUsername());
            properties.setProperty(InteractiveClient.PASSWORD, connection.getPassword());

            if (!connection.getUri().isEmpty()) {
                properties.setProperty(InteractiveClient.URI, connection.getUri());
                properties.setProperty(InteractiveClient.SSL_ENABLE, Boolean.valueOf(connection.isSsl()).toString().toUpperCase());
                properties.setProperty(InteractiveClient.LOCAL_MODE, "FALSE");
            } else {
                properties.setProperty(InteractiveClient.CONFIGURATION, connection.getConfiguration());
                properties.setProperty(InteractiveClient.URI, XmldbURI.EMBEDDED_SERVER_URI.toString());
            }
        });

        connectionDialog.setVisible(true);

        return properties;
    }

    public static void showErrorMessage(final String message, final Throwable t) {
        JScrollPane scroll = null;
        final JTextArea msgArea = new JTextArea(message);
        msgArea.setBorder(BorderFactory.createTitledBorder(Messages.getString("ClientFrame.214"))); //$NON-NLS-1$
        msgArea.setEditable(false);
        msgArea.setBackground(null);
        if (t != null) {
            final StringWriter out = new StringWriter();
            final PrintWriter writer = new PrintWriter(out);
            t.printStackTrace(writer);
            final JTextArea stacktrace = new JTextArea(out.toString(), 20, 50);
            stacktrace.setBackground(null);
            stacktrace.setEditable(false);
            scroll = new JScrollPane(stacktrace);
            scroll.setPreferredSize(new Dimension(250, 300));
            scroll.setBorder(BorderFactory
                    .createTitledBorder(Messages.getString("ClientFrame.215"))); //$NON-NLS-1$
        }
        final JOptionPane optionPane = new JOptionPane();
        optionPane.setMessage(new Object[]{msgArea, scroll});
        optionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
        final JDialog dialog = optionPane.createDialog(null, Messages.getString("ClientFrame.216")); //$NON-NLS-1$
        dialog.setResizable(true);
        dialog.pack();
        dialog.setVisible(true);
    }

    public static int showErrorMessageQuery(final String message, final Throwable t) {
        final JTextArea msgArea = new JTextArea(message);
        msgArea.setLineWrap(true);
        msgArea.setWrapStyleWord(true);
        msgArea.setEditable(false);
        msgArea.setBackground(null);
        JScrollPane scrollMsgArea = new JScrollPane(msgArea);
        scrollMsgArea.setPreferredSize(new Dimension(600, 300));
        scrollMsgArea.setBorder(BorderFactory
                .createTitledBorder(Messages.getString("ClientFrame.217"))); //$NON-NLS-1$

        JScrollPane scrollStacktrace = null;
        if (t != null) {
            try (final StringWriter out = new StringWriter();
                 final PrintWriter writer = new PrintWriter(out)) {
                t.printStackTrace(writer);
                final JTextArea stacktrace = new JTextArea(out.toString(), 20, 50);
                stacktrace.setLineWrap(true);
                stacktrace.setWrapStyleWord(true);
                stacktrace.setBackground(null);
                stacktrace.setEditable(false);
                scrollStacktrace = new JScrollPane(stacktrace);
                scrollStacktrace.setPreferredSize(new Dimension(600, 300));
                scrollStacktrace.setBorder(BorderFactory
                        .createTitledBorder(Messages.getString("ClientFrame.218"))); //$NON-NLS-1$
            } catch (final IOException ioe) {
                ioe.printStackTrace();
            }
        }

        final JOptionPane optionPane = new JOptionPane();
        optionPane.setMessage(new Object[]{scrollMsgArea, scrollStacktrace});
        optionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
        optionPane.setOptionType(JOptionPane.OK_CANCEL_OPTION);
        final JDialog dialog = optionPane.createDialog(null, Messages.getString("ClientFrame.219")); //$NON-NLS-1$
        dialog.setResizable(true);
        dialog.pack();
        dialog.setVisible(true);

        final Object result = optionPane.getValue();
        if (result == null) {
            return 2;
        }
        return ((Integer) optionPane.getValue()).intValue();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.WindowFocusListener#windowGainedFocus(java.awt.event.WindowEvent)
     */
    @Override
    public void windowGainedFocus(final WindowEvent e) {
        toFront();
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.WindowFocusListener#windowLostFocus(java.awt.event.WindowEvent)
     */
    @Override
    public void windowLostFocus(final WindowEvent e) {
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseClicked(final MouseEvent e) {
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseEntered(final MouseEvent e) {
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseExited(final MouseEvent e) {
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
     */
    @Override
    public void mousePressed(final MouseEvent e) {
        if (e.isPopupTrigger()) {
            shellPopup.show((Component) e.getSource(), e.getX(), e.getY());
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
     */
    @Override
    public void mouseReleased(final MouseEvent e) {
        if (e.isPopupTrigger()) {
            shellPopup.show((Component) e.getSource(), e.getX(), e.getY());
        }
    }

    static class BinaryFileFilter extends FileFilter {

        /* (non-Javadoc)
         * @see javax.swing.filechooser.FileFilter#getDescription()
         */
        @Override
        public String getDescription() {
            return Messages.getString("ClientFrame.220"); //$NON-NLS-1$
        }

        /* (non-Javadoc)
         * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
         */
        @Override
        public boolean accept(final File f) {
            if (f.isDirectory()) {
                return true;
            }
            return !MimeTable.getInstance().isXMLContent(f.getName());
        }
    }

    static class XMLFileFilter extends FileFilter {

        /* (non-Javadoc)
         * @see javax.swing.filechooser.FileFilter#getDescription()
         */
        @Override
        public String getDescription() {
            return Messages.getString("ClientFrame.221"); //$NON-NLS-1$
        }

        /* (non-Javadoc)
         * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
         */
        @Override
        public boolean accept(final File f) {
            if (f.isDirectory()) {
                return true;
            }
            return MimeTable.getInstance().isXMLContent(f.getName());
        }
    }

    private class FileListDropTargetListener implements DropTargetListener {

        @Override
        public void dragEnter(final DropTargetDragEvent dtde) {
        }

        @Override
        public void dragOver(final DropTargetDragEvent dtde) {
        }

        @Override
        public void dropActionChanged(final DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(final DropTargetEvent dte) {

        }

        @Override
        public void drop(final DropTargetDropEvent dtde) {
            try {

                dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

                final Transferable transferable = dtde.getTransferable();

                //should work for Win32 systems
                List files = getFilesWin32(transferable);

                //should work for *nix systems
                if (files == null) {
                    files = getFilesUnix(transferable);
                }

                if (files != null) {
                    uploadFiles(files);
                }
            } catch (final URISyntaxException | IOException | UnsupportedFlavorException | ClassNotFoundException use) {
                System.err.println("An exception occurred while dragging and dropping files: " + use.getMessage());
                use.printStackTrace();
            }
        }

        private List getFilesWin32(final Transferable transferable) throws UnsupportedFlavorException, IOException {
            return ((List) transferable.getTransferData(DataFlavor.javaFileListFlavor)).stream().map(File::toPath).collect(Collectors.toList());
        }

        private List getFilesUnix(final Transferable transferable) throws ClassNotFoundException, UnsupportedFlavorException, IOException, URISyntaxException {

            List files = null;
            final DataFlavor unixFileDataFlavour = new DataFlavor("text/uri-list;class=java.lang.String");
            final String data = (String) transferable.getTransferData(unixFileDataFlavour);
            for (final StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens(); ) {
                final String token = st.nextToken().trim();
                if (token.startsWith("#") || token.isEmpty()) {
                    // comment line, by RFC 2483
                    continue;
                }

                //lazy
                if (files == null) {
                    files = new ArrayList<>();
                }

                files.add(Paths.get(new URI(token)));
            }

            return files;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy