• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

evolvedbinary / elemental / 982

29 Apr 2025 08:34PM UTC coverage: 56.409% (+0.007%) from 56.402%
982

push

circleci

adamretter
[feature] Improve README.md badges

28451 of 55847 branches covered (50.94%)

Branch coverage included in aggregate %.

77468 of 131924 relevant lines covered (58.72%)

0.59 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.66
/exist-core/src/main/java/org/exist/client/ClientFrame.java
1
/*
2
 * Elemental
3
 * Copyright (C) 2024, Evolved Binary Ltd
4
 *
5
 * admin@evolvedbinary.com
6
 * https://www.evolvedbinary.com | https://www.elemental.xyz
7
 *
8
 * Use of this software is governed by the Business Source License 1.1
9
 * included in the LICENSE file and at www.mariadb.com/bsl11.
10
 *
11
 * Change Date: 2028-04-27
12
 *
13
 * On the date above, in accordance with the Business Source License, use
14
 * of this software will be governed by the Apache License, Version 2.0.
15
 *
16
 * Additional Use Grant: Production use of the Licensed Work for a permitted
17
 * purpose. A Permitted Purpose is any purpose other than a Competing Use.
18
 * A Competing Use means making the Software available to others in a commercial
19
 * product or service that: substitutes for the Software; substitutes for any
20
 * other product or service we offer using the Software that exists as of the
21
 * date we make the Software available; or offers the same or substantially
22
 * similar functionality as the Software.
23
 *
24
 * NOTE: Parts of this file contain code from 'The eXist-db Authors'.
25
 *       The original license header is included below.
26
 *
27
 * =====================================================================
28
 *
29
 * eXist-db Open Source Native XML Database
30
 * Copyright (C) 2001 The eXist-db Authors
31
 *
32
 * info@exist-db.org
33
 * http://www.exist-db.org
34
 *
35
 * This library is free software; you can redistribute it and/or
36
 * modify it under the terms of the GNU Lesser General Public
37
 * License as published by the Free Software Foundation; either
38
 * version 2.1 of the License, or (at your option) any later version.
39
 *
40
 * This library is distributed in the hope that it will be useful,
41
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
42
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
43
 * Lesser General Public License for more details.
44
 *
45
 * You should have received a copy of the GNU Lesser General Public
46
 * License along with this library; if not, write to the Free Software
47
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
48
 */
49
package org.exist.client;
50

51
import org.exist.SystemProperties;
52
import org.exist.backup.Backup;
53
import org.exist.backup.CreateBackupDialog;
54
import org.exist.backup.GuiRestoreServiceTaskListener;
55
import org.exist.client.security.EditPropertiesDialog;
56
import org.exist.client.security.ModeDisplay;
57
import org.exist.client.security.UserManagerDialog;
58
import org.exist.security.*;
59
import org.exist.security.SecurityManager;
60
import org.exist.security.internal.aider.SimpleACLPermissionAider;
61
import org.exist.storage.serializers.EXistOutputKeys;
62
import org.exist.util.FileUtils;
63
import org.exist.util.MimeTable;
64
import org.exist.util.SystemExitCodes;
65
import org.exist.util.crypto.digest.DigestType;
66
import org.exist.util.crypto.digest.MessageDigest;
67
import org.exist.util.serializer.SAXSerializer;
68
import org.exist.util.serializer.SerializerPool;
69
import org.exist.xmldb.*;
70
import org.exist.xquery.Constants;
71
import org.exist.xquery.util.URIUtils;
72
import org.xml.sax.SAXException;
73
import org.xmldb.api.DatabaseManager;
74
import org.xmldb.api.base.Collection;
75
import org.xmldb.api.base.Resource;
76
import org.xmldb.api.base.XMLDBException;
77
import org.xmldb.api.modules.XMLResource;
78

79
import javax.swing.*;
80
import javax.swing.border.BevelBorder;
81
import javax.swing.filechooser.FileFilter;
82
import javax.swing.table.AbstractTableModel;
83
import javax.swing.table.TableRowSorter;
84
import javax.swing.text.BadLocationException;
85
import javax.swing.text.DefaultStyledDocument;
86
import javax.swing.text.SimpleAttributeSet;
87
import javax.swing.text.StyleConstants;
88
import javax.xml.transform.OutputKeys;
89
import java.awt.*;
90
import java.awt.datatransfer.DataFlavor;
91
import java.awt.datatransfer.Transferable;
92
import java.awt.datatransfer.UnsupportedFlavorException;
93
import java.awt.dnd.*;
94
import java.awt.event.*;
95
import java.io.*;
96
import java.net.URI;
97
import java.net.URISyntaxException;
98
import java.net.URL;
99
import java.nio.file.Files;
100
import java.nio.file.Path;
101
import java.nio.file.Paths;
102
import java.util.*;
103
import java.util.List;
104
import java.util.concurrent.*;
105
import java.util.function.Consumer;
106
import java.util.function.Supplier;
107
import java.util.prefs.Preferences;
108
import java.util.stream.Collectors;
109

110
import static java.nio.charset.StandardCharsets.UTF_8;
111
import static javax.swing.JOptionPane.INFORMATION_MESSAGE;
112
import static org.exist.client.InteractiveClient.DATE_TIME_FORMATTER;
113
import static org.exist.util.FileUtils.humanSize;
114

115
/**
116
 * @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
117
 *
118
 * Main frame of the Elemental Java Admin client GUI
119
 */
120
public class ClientFrame extends JFrame implements WindowFocusListener, KeyListener, ActionListener, MouseListener {
121

122
    private static final long serialVersionUID = 1L;
123

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

128
    public static final int MAX_DISPLAY_LENGTH = 512000;
129
    public static final int MAX_HISTORY = 50;
130

131
    private static final SimpleAttributeSet promptAttrs = new SimpleAttributeSet();
1✔
132
    private static final SimpleAttributeSet defaultAttrs = new SimpleAttributeSet();
1✔
133
    static {
134
        StyleConstants.setForeground(promptAttrs, Color.blue);
1✔
135
        StyleConstants.setBold(promptAttrs, true);
1✔
136
        StyleConstants.setForeground(defaultAttrs, Color.black);
1✔
137
    }
138

139
    public static final String MULTIPLE_INDICATOR = "[...]";
140
    private static final String NON_APPLICABLE = "N/A";
141
    private static final String COLLECTION_MIME_TYPE = "elemental/collection";
1✔
142

143
    private int commandStart = 0;
×
144

145
    private boolean gotUp = false;
×
146
    private DefaultStyledDocument doc;
147
    private JLabel statusbar;
148
    private JTable fileman;
149
    private final ResourceTableModel resources = new ResourceTableModel();
×
150
    private JTextPane shell;
151
    private JPopupMenu shellPopup;
152
    private final ProcessRunnable processRunnable;
153
    private final Thread processThread;
154
    private Preferences preferences;
155

156
    private XmldbURI path = null;
×
157
    private Properties properties;
158
    private final InteractiveClient client;
159

160
    /**
161
     * Constructor.
162
     *
163
     * @param client Elemental client
164
     * @param path Database connection URL.
165
     * @param properties Configuration items.
166
     * @throws java.awt.HeadlessException Environment  does not support a keyboard, display, or mouse.
167
     */
168
    public ClientFrame(final InteractiveClient client, final XmldbURI path, final Properties properties) throws HeadlessException {
169
        super(Messages.getString("ClientFrame.3")); //$NON-NLS-1$
×
170
        this.path = path;
×
171
        this.properties = properties;
×
172
        this.client = client;
×
173
        this.processRunnable = new ProcessRunnable();
×
174
        this.processThread = client.newClientThread("process", processRunnable);
×
175

176
        this.setIconImage(InteractiveClient.getElementalIcon(getClass()).getImage());
×
177

178
        setupComponents();
×
179
        addWindowListener(new WindowAdapter() {
×
180
            @Override
181
            public void windowClosing(final WindowEvent ev) {
182
                close();
×
183
            }
×
184
        });
185
        pack();
×
186

187
        processThread.start();
×
188

189
        shell.requestFocus();
×
190

191
        preferences = Preferences.userNodeForPackage(ClientFrame.class);
×
192
    }
×
193

194
    private void setupComponents() {
195
        setJMenuBar(createMenuBar());
×
196

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

205
        url = getClass().getResource("icons/Refresh24.gif"); //$NON-NLS-1$
×
206
        button = new JButton(new ImageIcon(url));
×
207
        button.setToolTipText(Messages.getString("ClientFrame.7")); //$NON-NLS-1$
×
208
        button.addActionListener(e -> {
×
209
            try {
210
                client.reloadCollection();
×
211
            } catch (final XMLDBException e1) {
×
212
                showErrorMessage(e1.getMessage(), e1);
×
213
            }
214
        });
×
215
        toolbar.add(button);
×
216
        toolbar.addSeparator();
×
217

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

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

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

236
        url = getClass().getResource(Messages.getString("ClientFrame.14")); //$NON-NLS-1$
×
237
        button = new JButton(new ImageIcon(url));
×
238
        button.setToolTipText(Messages.getString("ClientFrame.15")); //$NON-NLS-1$
×
239
        button.addActionListener(e -> {
×
240
            try {
241
                setPermAction(e);
×
242
            } catch (final PermissionDeniedException pde) {
×
243
                showErrorMessage(pde.getMessage(), pde);
×
244
            }
245
        });
×
246
        toolbar.add(button);
×
247

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

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

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

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

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

278
        // create table for resources and collections
279
        fileman = new JTable();
×
280
        fileman.setModel(resources);
×
281
        fileman.setRowSorter(new TableRowSorter(resources));
×
282
        fileman.addMouseListener(new TableMouseListener());
×
283
        //fileman.setTransferHandler(new TransferHandler(){  
284
        //});
285

286
        fileman.setDropMode(DropMode.ON);
×
287
        final DropTarget filemanDropTarget = new DropTarget(fileman, DnDConstants.ACTION_COPY, new FileListDropTargetListener());
×
288
        fileman.setDropTarget(filemanDropTarget);
×
289

290
        fileman.setDefaultRenderer(Object.class, new HighlightedTableCellRenderer<ResourceTableModel>());
×
291
        JScrollPane scroll = new JScrollPane(fileman);
×
292
        scroll.setMinimumSize(new Dimension(300, 150));
×
293
        split.setLeftComponent(scroll);
×
294

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

300
        // shell window
301
        doc = new DefaultStyledDocument();
×
302
        shell = new JTextPane(doc);
×
303
        shell.setContentType("text/plain; charset=UTF-8"); //$NON-NLS-1$
×
304
        shell.setFont(new Font("Monospaced", Font.PLAIN, 12)); //$NON-NLS-1$
×
305
        shell.setMargin(new Insets(7, 5, 7, 5));
×
306
        shell.addKeyListener(this);
×
307
        shell.addMouseListener(this);
×
308

309
        scroll = new JScrollPane(shell);
×
310

311
        split.setRightComponent(scroll);
×
312

313
        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$
×
314
        statusbar.setMinimumSize(new Dimension(400, 15));
×
315
        statusbar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
×
316

317
        getContentPane().add(split, BorderLayout.CENTER);
×
318
        getContentPane().add(toolbar, BorderLayout.NORTH);
×
319
        getContentPane().add(statusbar, BorderLayout.SOUTH);
×
320
    }
×
321

322
    private JMenuBar createMenuBar() {
323
        final JMenuBar menubar = new JMenuBar();
×
324

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

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

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

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

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

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

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

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

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

378
        fileMenu.addSeparator();
×
379

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

386
        item = new JMenuItem(Messages.getString("ClientFrame.50")); //$NON-NLS-1$
×
387
        item.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P,
×
388
                Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
×
389
        item.addActionListener(e -> {
×
390
            try {
391
                setPermAction(e);
×
392
            } catch (final PermissionDeniedException pde) {
×
393
                showErrorMessage(pde.getMessage(), pde);
×
394
            }
395
        });
×
396
        fileMenu.add(item);
×
397

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

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

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

415
        toolsMenu.addSeparator();
×
416

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

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

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

440
        toolsMenu.addSeparator();
×
441

442
        item = new JMenuItem(Messages.getString("ClientFrame.62a"));
×
443
        item.addActionListener(e -> {
×
444
            try {
445
                final DatabaseInstanceManager service = client.current.getService(DatabaseInstanceManager.class);
×
446
                service.enterServiceMode();
×
447
            } catch (final XMLDBException ex) {
×
448
                showErrorMessage(ex.getMessage(), ex);
×
449
            }
450
        });
×
451
        toolsMenu.add(item);
×
452

453
        item = new JMenuItem(Messages.getString("ClientFrame.62b"));
×
454
        item.addActionListener(e -> {
×
455
            try {
456
                final DatabaseInstanceManager service = client.current.getService(DatabaseInstanceManager.class);
×
457
                service.exitServiceMode();
×
458
            } catch (final XMLDBException ex) {
×
459
                showErrorMessage(ex.getMessage(), ex);
×
460
            }
461
        });
×
462
        toolsMenu.add(item);
×
463

464
        toolsMenu.addSeparator();
×
465

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

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

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

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

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

495

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

500
        JCheckBoxMenuItem check = new JCheckBoxMenuItem(Messages.getString("ClientFrame.81"), "yes".equals(properties.getProperty(OutputKeys.INDENT))); //$NON-NLS-1$
×
501
        check.addActionListener(event -> {
×
502
            properties.setProperty(OutputKeys.INDENT,
×
503
                    ((JCheckBoxMenuItem) event.getSource()).isSelected()
×
504
                            ? "yes" //$NON-NLS-1$
×
505
                            : "no"); //$NON-NLS-1$
×
506
            ClientAction.call(client::getResources, e -> showErrorMessage(e.getMessage(), e));
×
507
        });
×
508
        optionsMenu.add(check);
×
509

510
        check = new JCheckBoxMenuItem(Messages.getString("ClientFrame.85"), "yes".equals(properties.getProperty(EXistOutputKeys.EXPAND_XINCLUDES))); //$NON-NLS-1$
×
511
        check.addActionListener(event -> {
×
512
            properties.setProperty(EXistOutputKeys.EXPAND_XINCLUDES,
×
513
                    ((JCheckBoxMenuItem) event.getSource()).isSelected()
×
514
                            ? "yes" //$NON-NLS-1$
×
515
                            : "no"); //$NON-NLS-1$
×
516
            ClientAction.call(client::getResources, e -> showErrorMessage(e.getMessage(), e));
×
517
        });
×
518
        optionsMenu.add(check);
×
519

520

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

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

529
        return menubar;
×
530
    }
531

532
    public void reconnectClient(final Properties loginData) {
533
        if (loginData == null || loginData.isEmpty()) {
×
534
            return;
×
535
        }
536

537
        // make a backup of the current properties
538
        final Properties oldProps = new Properties();
×
539
        oldProps.putAll(properties);
×
540

541
        // update the properties with the new login data
542
        properties.putAll(loginData);
×
543
        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$
×
544

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

552
            // restore the previous properties
553
            properties = oldProps;
×
554
            try {
555
                // attempt to re-connect with previous login data
556
                client.connect();
×
557
            } catch (final Exception uu) {
×
558
                showErrorMessage(Messages.getString("ClientFrame.78") + properties.getProperty(InteractiveClient.URI), uu); //$NON-NLS-1$ //$NON-NLS-2$
×
559
            }
560
        }
561
    }
×
562

563
    public void setPath(final XmldbURI currentPath) {
564
        path = currentPath;
×
565
    }
×
566

567
    protected void displayPrompt() {
568
        final String pathString = path.getCollectionPath();
×
569
        try {
570
            commandStart = doc.getLength();
×
571
            final String prompt = Messages.getString("ClientFrame.91");
×
572
            doc.insertString(commandStart, prompt, promptAttrs); //$NON-NLS-1$
×
573
            commandStart += prompt.length();
×
574
            doc.insertString(commandStart, pathString + '>', promptAttrs);
×
575
            commandStart += pathString.length() + 1;
×
576
            doc.insertString(commandStart++, Messages.getString("ClientFrame.92"), defaultAttrs); //$NON-NLS-1$
×
577
            shell.setCaretPosition(commandStart);
×
578
        } catch (final BadLocationException e) {
×
579
            showErrorMessage(e.getMessage(), e);
×
580
        }
581
    }
×
582

583
    protected void display(final String message) {
584
        try {
585
            commandStart = doc.getLength();
×
586
            if (commandStart > MAX_DISPLAY_LENGTH) {
×
587
                doc.remove(0, MAX_DISPLAY_LENGTH);
×
588
                commandStart = doc.getLength();
×
589
            }
590
            doc.insertString(commandStart, message, defaultAttrs);
×
591
            commandStart = doc.getLength();
×
592
            shell.setCaretPosition(commandStart);
×
593

594
        } catch (final BadLocationException e) {
×
595
            showErrorMessage(e.getMessage(), e);
×
596
        }
597
    }
×
598

599
    protected void setResources(final List<ResourceDescriptor> rows) {
600
        resources.setData(rows);
×
601
    }
×
602

603
    protected void setStatus(final String message) {
604
        statusbar.setText(message);
×
605
    }
×
606

607
    protected void setEditable(final boolean enabled) {
608
        shell.setEditable(enabled);
×
609
        shell.setVisible(enabled);
×
610
    }
×
611

612
    /**
613
     * @see java.awt.event.KeyListener#keyPressed(java.awt.event.KeyEvent)
614
     */
615
    @Override
616
    public void keyPressed(final KeyEvent e) {
617
        type(e);
×
618
        gotUp = false;
×
619
    }
×
620

621
    /**
622
     * @see java.awt.event.KeyListener#keyReleased(java.awt.event.KeyEvent)
623
     */
624
    @Override
625
    public void keyReleased(final KeyEvent e) {
626
        gotUp = true;
×
627
        type(e);
×
628
    }
×
629

630
    /*
631
     * (non-Javadoc)
632
     *
633
     * @see java.awt.event.KeyListener#keyTyped(java.awt.event.KeyEvent)
634
     */
635
    @Override
636
    public void keyTyped(final KeyEvent e) {
637
        type(e);
×
638
    }
×
639

640
    private synchronized void type(final KeyEvent e) {
641
        switch (e.getKeyCode()) {
×
642
            case KeyEvent.VK_ENTER:
643
                if (e.getID() == KeyEvent.KEY_PRESSED && gotUp) {
×
644
                    enter();
×
645
                }
646
                e.consume();
×
647
                break;
×
648
            case KeyEvent.VK_HOME:
649
                shell.setCaretPosition(commandStart);
×
650
                e.consume();
×
651
                break;
×
652
            case KeyEvent.VK_LEFT:
653
            case KeyEvent.VK_DELETE:
654
            case KeyEvent.VK_BACK_SPACE:
655
                if (shell.getCaretPosition() <= commandStart) {
×
656
                    e.consume();
×
657
                }
658
                break;
×
659
            case KeyEvent.VK_UP:
660
                if (e.getID() == KeyEvent.KEY_PRESSED) {
×
661
                    historyBack();
×
662
                }
663
                e.consume();
×
664
                break;
×
665
            case KeyEvent.VK_DOWN:
666
                if (e.getID() == KeyEvent.KEY_PRESSED) {
×
667
                    historyForward();
×
668
                }
669
                e.consume();
×
670
                break;
×
671
            default:
672
                if ((e.getModifiers() & (InputEvent.CTRL_MASK
×
673
                        | InputEvent.META_MASK | InputEvent.ALT_MASK)) == 0) {
674
                    if (shell.getCaretPosition() < commandStart) {
×
675
                        shell.setCaretPosition(doc.getLength());
×
676
                    }
677
                }
678
                if (e.paramString().contains(Messages.getString("ClientFrame.93"))) { //$NON-NLS-1$
×
679
                    if (shell.getCaretPosition() <= commandStart) {
×
680
                        e.consume();
×
681
                    }
682
                }
683
                break;
684
        }
685
    }
×
686

687
    /**
688
     * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
689
     */
690
    @Override
691
    public void actionPerformed(final ActionEvent e) {
692
        final String cmd = e.getActionCommand();
×
693
        if (cmd.equals(CUT)) {
×
694
            shell.cut();
×
695
        } else if (cmd.equals(COPY)) {
×
696
            shell.copy();
×
697
        } else if (cmd.equals(PASTE)) {
×
698
            shell.paste();
×
699
        }
700
    }
×
701

702
    private void newBlankDocument(final ActionEvent e) {
703
        final JFrame dialog = new NewResourceDialog(client);
×
704
        dialog.setVisible(true);
×
705
    }
×
706

707

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

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

722
    /**
723
     * Returns an array of user-selected resources.
724
     */
725
    private ResourceDescriptor[] getSelectedResources() {
726
        final int[] selectedRows = fileman.getSelectedRows();
×
727
        final ResourceDescriptor[] res = new ResourceDescriptor[selectedRows.length];
×
728

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

733
        return res;
×
734
    }
735

736
    private void removeAction(final ActionEvent ev) {
737

738
        final ResourceDescriptor[] res = getSelectedResources();
×
739
        final Collection removeRootCollection = client.current;
×
740
        // String cmd;
741
        if (JOptionPane.showConfirmDialog(this,
×
742
                Messages.getString("ClientFrame.104") + Messages.getString("ClientFrame.105"), //$NON-NLS-1$ //$NON-NLS-2$
×
743
                Messages.getString("ClientFrame.106"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { //$NON-NLS-1$
×
744
            final Runnable removeTask = () -> {
×
745
                final ProgressMonitor monitor = new ProgressMonitor(ClientFrame.this, Messages.getString("ClientFrame.107"), Messages.getString("ClientFrame.108"), 1, res.length); //$NON-NLS-1$ //$NON-NLS-2$
×
746
                monitor.setMillisToDecideToPopup(500);
×
747
                monitor.setMillisToPopup(500);
×
748
                for (int i = 0; i < res.length; i++) {
×
749
                    final ResourceDescriptor resource = res[i];
×
750
                    if (resource.isCollection()) {
×
751
                        try {
752
                            final EXistCollectionManagementService mgtService = removeRootCollection
×
753
                                    .getService(EXistCollectionManagementService.class);
×
754
                            mgtService
×
755
                                    .removeCollection(resource.getName());
×
756
                        } catch (final XMLDBException e) {
×
757
                            showErrorMessage(e.getMessage(), e);
×
758
                        }
759
                    } else {
×
760
                        try {
761
                            final Resource res1 = removeRootCollection
×
762
                                    .getResource(resource.getName().toString());
×
763
                            removeRootCollection.removeResource(res1);
×
764
                        } catch (final XMLDBException e) {
×
765
                            showErrorMessage(e.getMessage(), e);
×
766
                        }
767
                    }
768
                    monitor.setProgress(i + 1);
×
769
                    if (monitor.isCanceled()) {
×
770
                        return;
×
771
                    }
772
                }
773

774
                ClientAction.call(client::getResources, e -> showErrorMessage(e.getMessage(), e));
×
775
            };
×
776
            client.newClientThread("remove", removeTask).start();
×
777
        }
778
    }
×
779

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

783
        PrettyXmldbURI[] collections;
784

785
        //get an array of collection paths
786
        try {
787
            final Collection root = client.getCollection(XmldbURI.ROOT_COLLECTION);
×
788
            final List<PrettyXmldbURI> alCollections = getCollections(root, new ArrayList<>());
×
789
            collections = new PrettyXmldbURI[alCollections.size()];
×
790
            alCollections.toArray(collections);
×
791
        } catch (final XMLDBException e) {
×
792
            showErrorMessage(e.getMessage(), e);
×
793
            return;
×
794
        }
795

796
        //prompt the user for a destination collection from the list
797
        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$
×
798
        if (val == null) {
×
799
            return;
×
800
        }
801

802
        final XmldbURI destinationPath = ((PrettyXmldbURI) val).getTargetURI();
×
803
        final Runnable moveTask = () -> {
×
804
            try {
805
                final EXistCollectionManagementService service = client.current.getService(EXistCollectionManagementService.class);
×
806
                for (ResourceDescriptor re : res) {
×
807
                    setStatus(Messages.getString("ClientFrame.115") + re.getName() + Messages.getString("ClientFrame.116") + destinationPath + Messages.getString("ClientFrame.117")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
×
808
                    if (re.isCollection()) {
×
809
                        service.move(re.getName(), destinationPath, null);
×
810
                    } else {
×
811
                        service.moveResource(re.getName(), destinationPath, null);
×
812
                    }
813
                }
814
                client.reloadCollection();
×
815
            } catch (final XMLDBException e) {
×
816
                showErrorMessage(e.getMessage(), e);
×
817
            }
818
            setStatus(Messages.getString("ClientFrame.118")); //$NON-NLS-1$
×
819
        };
×
820
        client.newClientThread("move", moveTask).start();
×
821
    }
×
822

823
    private void renameAction(final ActionEvent ev) {
824
        final ResourceDescriptor[] res = getSelectedResources();
×
825
        String inputValue = "";
×
826
        try {
827
            inputValue = res[0].getName().toString();
×
828
        } catch (Exception npe) {
×
829
        }
830
        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$
×
831

832
        if (val == null) {
×
833
            return;
×
834
        }
835

836
        XmldbURI parseIt;
837
        try {
838
            parseIt = URIUtils.encodeXmldbUriFor((String) val);
×
839
        } catch (final URISyntaxException e) {
×
840
            showErrorMessage(Messages.getString("ClientFrame.121") + e.getMessage(), e); //$NON-NLS-1$
×
841
            return;
×
842
        }
843
        final XmldbURI destinationFilename = parseIt;
×
844
        final Runnable renameTask = () -> {
×
845
            try {
846
                final EXistCollectionManagementService service = client.current.getService(EXistCollectionManagementService.class);
×
847
                boolean changed = false;
×
848
                for (final ResourceDescriptor re : res) {
×
849
                    if (!re.getName().equals(destinationFilename)) {
×
850
                        setStatus(Messages.getString("ClientFrame.124") + re.getName() + Messages.getString("ClientFrame.125") + destinationFilename + Messages.getString("ClientFrame.126")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
×
851
                        if (re.isCollection()) {
×
852
                            service.move(re.getName(), null, destinationFilename);
×
853
                        } else {
×
854
                            service.moveResource(re.getName(), null, destinationFilename);
×
855
                        }
856
                        changed = true;
×
857
                    }
858
                }
859
                if (changed) {
×
860
                    client.reloadCollection();
×
861
                }
862
            } catch (final XMLDBException e) {
×
863
                showErrorMessage(e.getMessage(), e);
×
864
            }
865
            setStatus(Messages.getString("ClientFrame.127")); //$NON-NLS-1$
×
866
        };
×
867
        client.newClientThread("rename", renameTask).start();
×
868
    }
×
869

870
    private void copyAction(final ActionEvent ev) {
871

872
        final ResourceDescriptor[] res = getSelectedResources();
×
873
        PrettyXmldbURI[] collections;
874

875
        //get an array of collection paths
876
        try {
877
            final Collection root = client.getCollection(XmldbURI.ROOT_COLLECTION);
×
878
            final List<PrettyXmldbURI> alCollections = getCollections(root, new ArrayList<>());
×
879
            collections = new PrettyXmldbURI[alCollections.size()];
×
880
            alCollections.toArray(collections);
×
881
        } catch (final XMLDBException e) {
×
882
            showErrorMessage(e.getMessage(), e);
×
883
            return;
×
884
        }
885

886
        //prompt the user for a destination collection from the list
887
        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$
×
888
        if (val == null) {
×
889
            return;
×
890
        }
891

892
        final XmldbURI destinationPath = ((PrettyXmldbURI) val).getTargetURI();
×
893

894
        final Runnable moveTask = () -> {
×
895
            try {
896
                final EXistCollectionManagementService service = client.current.getService(EXistCollectionManagementService.class);
×
897
                for (ResourceDescriptor re : res) {
×
898

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

908
                    setStatus(Messages.getString("ClientFrame.132") + re.getName() + Messages.getString("ClientFrame.133") + destinationPath + Messages.getString("ClientFrame.134")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
×
909
                    if (re.isCollection()) {
×
910
                        service.copy(re.getName(), destinationPath, null);
×
911
                    } else {
×
912
                        service.copyResource(re.getName(), destinationPath, null);
×
913
                    }
914
                }
915
                client.reloadCollection();
×
916
            } catch (final XMLDBException e) {
×
917
                showErrorMessage(e.getMessage(), e);
×
918
            }
919
            setStatus(Messages.getString("ClientFrame.135")); //$NON-NLS-1$
×
920
        };
×
921
        client.newClientThread("move", moveTask).start();
×
922
    }
×
923

924
    private ArrayList<PrettyXmldbURI> getCollections(final Collection root, final ArrayList<PrettyXmldbURI> collectionsList) throws XMLDBException {
925
        collectionsList.add(new PrettyXmldbURI(XmldbURI.create(root.getName())));
×
926
        Collection child = null;
×
927
        for (String childCollection : root.listChildCollections()) {
×
928
            try {
929
                child = root.getChildCollection(childCollection);
×
930
            } catch (final XMLDBException xmldbe) {
×
931
                if (xmldbe.getCause() instanceof PermissionDeniedException) {
×
932
                    continue;
×
933
                } else {
934
                    throw xmldbe;
×
935
                }
936
            } catch (Exception npe) {
×
937
                System.out.println("Corrupted resource/collection skipped: " + child != null ? child.getName() != null ? child.getName() : "unknown" : "unknown");
×
938
                continue;
×
939
            }
940
            try {
941
                getCollections(child, collectionsList);
×
942
            } catch (Exception ee) {
×
943
                System.out.println("Corrupted resource/collection skipped: " + child != null ? child.getName() != null ? child.getName() : "unknown" : "unknown");
×
944
                continue;
945
            }
946
        }
947
        return collectionsList;
×
948
    }
949

950
    private void reindexAction(final ActionEvent ev) {
951
        final int[] selRows = fileman.getSelectedRows();
×
952
        final ResourceDescriptor[] res;
953
        if (selRows.length == 0) {
×
954
            res = new ResourceDescriptor[1];
×
955
            res[0] = new ResourceDescriptor.Collection(client.path);
×
956
        } else {
×
957
            res = new ResourceDescriptor[selRows.length];
×
958

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

968
        if (JOptionPane.showConfirmDialog(this,
×
969
                Messages.getString("ClientFrame.138"), //$NON-NLS-1$
×
970
                Messages.getString("ClientFrame.139"), JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { //$NON-NLS-1$
×
971
            final ResourceDescriptor collections[] = res;
×
972
            final Runnable reindexThread = () -> {
×
973
                ClientFrame.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
×
974
                final IndexQueryService service;
975
                try {
976
                    service = client.current.getService(IndexQueryService.class);
×
977
                    for (final ResourceDescriptor next : collections) {
×
978
                        setStatus(Messages.getString("ClientFrame.142") + next.getName() + Messages.getString("ClientFrame.143")); //$NON-NLS-1$ //$NON-NLS-2$
×
979
                        service.reindexCollection(next.getName());
×
980
                    }
981
                    setStatus(Messages.getString("ClientFrame.144")); //$NON-NLS-1$
×
982
                } catch (final XMLDBException e) {
×
983
                    showErrorMessage(e.getMessage(), e);
×
984
                }
985
                ClientFrame.this.setCursor(Cursor.getDefaultCursor());
×
986
            };
×
987
            client.newClientThread("reindex", reindexThread).start();
×
988
        }
989
    }
×
990

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

1002
            uploadFiles(FileUtils.asPathsList(chooser.getSelectedFiles()));
×
1003
        }
1004
    }
×
1005

1006
    private void uploadFiles(final List<Path> files) {
1007
        if (files != null && !files.isEmpty()) {
×
1008
            final Runnable uploadTask = () -> {
×
1009
                final UploadDialog upload = new UploadDialog();
×
1010
                final Consumer<XMLDBException> failureAction = e ->
×
1011
                        showErrorMessage(Messages.getString("ClientFrame.147") + e.getMessage(), e);
×
1012
                ClientAction.call(() -> client.parse(files, upload), failureAction);
×
1013
                ClientAction.call(client::getResources, failureAction);
×
1014
            };
×
1015
            client.newClientThread("upload", uploadTask).start();
×
1016
        }
1017
    }
×
1018

1019
    private boolean deleteDirectory(final Path target) {
1020
        try {
1021
            FileUtils.delete(target);
×
1022
            return true;
×
1023
        } catch (final IOException e) {
×
1024
            return false;
×
1025
        }
1026

1027
    }
1028

1029
    private void backupAction(final ActionEvent ev) {
1030

1031
        //get the collection to highlight in the backup dialog
1032
        final String defaultSelectedCollection;
1033
        final ResourceDescriptor selResources[] = getSelectedResources();
×
1034
        if (selResources != null) {
×
1035
            if (selResources.length == 1 && selResources[0].isCollection()) {
×
1036
                //use the selected collection
1037
                defaultSelectedCollection = path.toString() + "/" + selResources[0].getName().toString();
×
1038
            } else {
×
1039
                //use the current collection
1040
                defaultSelectedCollection = path.toString();
×
1041
            }
1042
        } else {
×
1043
            defaultSelectedCollection = path.toString();
×
1044
        }
1045

1046
        final CreateBackupDialog dialog = new CreateBackupDialog(
×
1047
                properties.getProperty(InteractiveClient.URI, "xmldb:exist://"),
×
1048
                properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER),
×
1049
                properties.getProperty(InteractiveClient.PASSWORD, null),
×
1050
                Paths.get(preferences.get("directory.backup", System.getProperty("user.home"))),
×
1051
                defaultSelectedCollection
×
1052
        );
1053

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

1056
            final String collection = dialog.getCollection();
×
1057
            final String backuptarget = dialog.getBackupTarget();
×
1058
            final boolean deduplicateBlobs = dialog.getDeduplicateBlobs();
×
1059

1060
            final Path target = Paths.get(backuptarget).normalize();
×
1061
            if (Files.exists(target)) {
×
1062
                final int response = JOptionPane.showConfirmDialog(this,
×
1063
                        String.format("%s %s %s", Messages.getString("CreateBackupDialog.6a"), backuptarget, Messages.getString("CreateBackupDialog.6b")),
×
1064
                        Messages.getString("CreateBackupDialog.6c"), JOptionPane.YES_NO_OPTION);
×
1065

1066
                if (response == JOptionPane.YES_OPTION) {
×
1067
                    // User wants file/directory to be removed
1068
                    deleteDirectory(target);
×
1069
                } else {
×
1070
                    JOptionPane.showMessageDialog(null, "Backup aborted, backup has not been deleted.");
×
1071
                    return;
×
1072
                }
1073
            }
1074

1075
            try {
1076
                final Backup backup = new Backup(
×
1077
                        properties.getProperty(InteractiveClient.USER, SecurityManager.DBA_USER),
×
1078
                        properties.getProperty(InteractiveClient.PASSWORD, null), Paths.get(backuptarget),
×
1079
                        XmldbURI.xmldbUriFor(properties.getProperty(InteractiveClient.URI, "xmldb:exist://") + collection),
×
1080
                        null,
×
1081
                        deduplicateBlobs
×
1082
                );
1083
                backup.backup(true, this);
×
1084
            } catch (final XMLDBException | IOException | SAXException | URISyntaxException e) {
×
1085
                showErrorMessage(e.getClass().getSimpleName() + ": " + e.getMessage(), e);
×
1086
            }
1087
        }
1088
    }
×
1089

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

1098
        if (chooser.showDialog(null, Messages.getString("ClientFrame.169")) == JFileChooser.APPROVE_OPTION) { //$NON-NLS-1$
×
1099
            final Path f = chooser.getSelectedFile().toPath();
×
1100
            preferences.put("directory.backup", chooser.getCurrentDirectory().getAbsolutePath());
×
1101
            final JPanel askPass = new JPanel(new BorderLayout());
×
1102
            askPass.add(new JLabel(Messages.getString("ClientFrame.170")), BorderLayout.NORTH); //$NON-NLS-1$
×
1103
            final JPasswordField passInput = new JPasswordField(25);
×
1104
            final JCheckBox overwriteCb = new JCheckBox(Messages.getString("ClientFrame.227"), false);
×
1105
            askPass.add(passInput, BorderLayout.CENTER);
×
1106
            askPass.add(overwriteCb, BorderLayout.SOUTH);
×
1107
            if (JOptionPane.showOptionDialog(this, askPass, Messages.getString("ClientFrame.171"), //$NON-NLS-1$
×
1108
                    JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
×
1109
                    null, null, null) == JOptionPane.YES_OPTION) {
×
1110
                final String newDbaPass = passInput.getPassword().length == 0 ? null : new String(passInput.getPassword());
×
1111
                final String restoreFile = f.toAbsolutePath().toString();
×
1112
                final boolean overwriteApps = overwriteCb.isSelected();
×
1113
                final GuiRestoreServiceTaskListener listener = new GuiRestoreServiceTaskListener(this);
×
1114
                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);
×
1115
            }
1116
        }
1117
    }
×
1118

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

1122
        final Runnable restoreTask = () -> {
×
1123

1124
            try {
1125
                final XmldbURI dbUri;
1126
                if(!uri.endsWith(XmldbURI.ROOT_COLLECTION)) {
×
1127
                    dbUri = XmldbURI.xmldbUriFor(uri + XmldbURI.ROOT_COLLECTION);
×
1128
                } else {
×
1129
                    dbUri = XmldbURI.xmldbUriFor(uri);
×
1130
                }
1131

1132
                final Collection collection = DatabaseManager.getCollection(dbUri.toString(), username, password);
×
1133
                final EXistRestoreService service = collection.getService(EXistRestoreService.class);
×
1134
                service.restore(f.toAbsolutePath().toString(), dbaPassword, listener, overwriteApps);
×
1135

1136
                if (JOptionPane.showConfirmDialog(null, Messages.getString("ClientFrame.223"), Messages.getString("ClientFrame.224"),
×
1137
                        JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
×
1138
                    setStatus(Messages.getString("ClientFrame.225"));
×
1139
                    repairRepository(client.getCollection());
×
1140
                    setStatus(Messages.getString("ClientFrame.226"));
×
1141
                }
1142

1143
                listener.enableDismissDialogButton();
×
1144

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

1149
                SwingUtilities.invokeAndWait(() -> {
×
1150
                    try {
1151
                        client.reloadCollection();
×
1152
                    } catch (final XMLDBException xe) {
×
1153
                        xe.printStackTrace();
×
1154
                    }
1155
                });
×
1156
            } catch (final Exception e) {
×
1157
                showErrorMessage(Messages.getString("ClientFrame.181") + e.getMessage(), e); //$NON-NLS-1$
×
1158
            } finally {
1159
                if (listener.hasProblems()) {
×
1160
                    showErrorMessage(Messages.getString("ClientFrame.181") + listener.getAllProblems(), null);
×
1161
                }
1162
            }
1163
        };
×
1164

1165
        client.newClientThread("restore", restoreTask).start();
×
1166
    }
×
1167

1168
    public static void repairRepository(Collection collection) throws XMLDBException {
1169
        final EXistXQueryService service = collection.getService(EXistXQueryService.class);
×
1170
        service.query("import module namespace repair=\"http://exist-db.org/xquery/repo/repair\"\n" +
×
1171
                "at \"resource:org/exist/xquery/modules/expathrepo/repair.xql\";\n" +
1172
                "repair:clean-all(),\n" +
1173
                "repair:repair()");
1174
    }
×
1175

1176
    public UserManagementService getUserManagementService() throws XMLDBException {
1177
        final Collection collection = client.getCollection();
×
1178
        return collection.getService(UserManagementService.class);
×
1179
    }
1180

1181
    private void editUsersAction(final ActionEvent ev) {
1182
        try {
1183
            final UserManagementService userManagementService = getUserManagementService();
×
1184

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

1188
        } catch (final XMLDBException e) {
×
1189
            showErrorMessage(Messages.getString("ClientFrame.185"), e); //$NON-NLS-1$
×
1190
            e.printStackTrace();
×
1191
        }
1192
    }
×
1193

1194
    private void exportAction(final ActionEvent ev) {
1195
        if (fileman.getSelectedRowCount() == 0) {
×
1196
            return;
×
1197
        }
1198
        final int[] rows = fileman.getSelectedRows();
×
1199
        for (final int row : rows) {
×
1200
            final ResourceDescriptor desc = resources.getRow(fileman.convertRowIndexToModel(row));
×
1201
            if (desc.isCollection()) {
×
1202
                continue;
×
1203
            }
1204

1205
            final JFileChooser chooser = new JFileChooser(preferences.get("directory.last", System.getProperty("user.dir")));
×
1206
            chooser.setMultiSelectionEnabled(false);
×
1207
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
×
1208
            chooser.setSelectedFile(Paths.get(desc.getName().getCollectionPath()).toFile());
×
1209
            if (chooser.showDialog(this, "Select file for export") == JFileChooser.APPROVE_OPTION) {
×
1210
                preferences.put("directory.last", chooser.getCurrentDirectory().getAbsolutePath());
×
1211
                final Path file = chooser.getSelectedFile().toPath();
×
1212
                if (Files.exists(file)
×
1213
                        && JOptionPane.showConfirmDialog(this,
×
1214
                        "File exists. Overwrite?", "Overwrite?",
×
1215
                        JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
×
1216
                    return;
×
1217
                }
1218
                final Resource resource;
1219
                final SAXSerializer contentSerializer;
1220
                try {
1221
                    final Collection collection = client.getCollection();
×
1222
                    resource = collection
×
1223
                            .getResource(desc.getName().toString());
×
1224
                    if (resource instanceof ExtendedResource) {
×
1225
                        try(final OutputStream os = new BufferedOutputStream(Files.newOutputStream(file))) {
×
1226
                            ((ExtendedResource) resource).getContentIntoAStream(os);
×
1227
                        }
1228
                    } else {
1229
                        contentSerializer = (SAXSerializer) SerializerPool
×
1230
                                .getInstance()
×
1231
                                .borrowObject(SAXSerializer.class);
×
1232
                        try(final Writer writer = Files.newBufferedWriter(file, UTF_8)) {
×
1233
                            // write resource to contentSerializer
1234
                            contentSerializer.setOutput(writer, properties);
×
1235
                            ((EXistResource) resource)
×
1236
                                    .setLexicalHandler(contentSerializer);
×
1237
                            ((XMLResource) resource)
×
1238
                                    .getContentAsSAX(contentSerializer);
×
1239
                        } finally {
1240
                            SerializerPool.getInstance().returnObject(contentSerializer);
×
1241
                        }
1242
                    }
1243
                    //TODO finally close os
1244
                } catch (final Exception e) {
×
1245
                    System.err.println("An exception occurred" + e.getMessage());
×
1246
                    e.printStackTrace();
×
1247
                }
1248
            }
1249
        }
1250
    }
×
1251

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

1257
    private void editTriggersAction(final ActionEvent ev) {
1258
        final TriggersDialog dialog = new TriggersDialog("Edit Triggers", client);
×
1259
        dialog.setVisible(true);
×
1260
    }
×
1261

1262
    private void findAction(final ActionEvent ev) {
1263
        final Collection collection = client.getCollection();
×
1264
        final QueryDialog dialog = new QueryDialog(client, collection, properties);
×
1265
        dialog.setVisible(true);
×
1266
    }
×
1267

1268
    private void setPermAction(final ActionEvent ev) throws PermissionDeniedException {
1269
        if (fileman.getSelectedRowCount() == 0) {
×
1270
            return;
×
1271
        }
1272
        try {
1273
            final Collection collection = client.getCollection();
×
1274
            final UserManagementService service = getUserManagementService();
×
1275

1276
            String name = null;
×
1277
            String created = null;
×
1278
            String modified = null;
×
1279
            String size = null;
×
1280
            String messageDigestType = null;
×
1281
            String messageDigestValue = null;
×
1282
            String mimeType = null;
×
1283
            String owner = null;
×
1284
            String group = null;
×
1285
            ModeDisplay mode = null;
×
1286
            SimpleACLPermissionAider acl = null;
×
1287

1288
            final List<ResourceDescriptor> selected = new ArrayList<>();
×
1289

1290
            boolean firstPerm = true;
×
1291
            for (final int row : fileman.getSelectedRows()) {
×
1292
                final ResourceDescriptor selectedRow = resources.getRow(row);
×
1293

1294
                selected.add(selectedRow);
×
1295

1296
                final XmldbURI thisName = selectedRow.getName();
×
1297
                final String thisCreated;
1298
                final String thisModified;
1299
                final String thisMessageDigestType;
1300
                final String thisMessageDigestValue;
1301
                final String thisSize;
1302
                final String thisMimeType;
1303
                final Permission thisPerm;
1304

1305
                if (selectedRow.isCollection()) {
×
1306
                    final Collection coll = collection.getChildCollection(thisName.toString());
×
1307
                    thisCreated = DATE_TIME_FORMATTER.format(coll.getCreationTime());
×
1308
                    thisModified = NON_APPLICABLE;
×
1309
                    thisMimeType = COLLECTION_MIME_TYPE;
×
1310
                    thisMessageDigestType = NON_APPLICABLE;
×
1311
                    thisMessageDigestValue = NON_APPLICABLE;
×
1312
                    thisSize = NON_APPLICABLE;
×
1313
                    thisPerm = service.getPermissions(coll);
×
1314
                } else {
×
1315
                    final Resource res = collection.getResource(thisName.toString());
×
1316
                    thisCreated = DATE_TIME_FORMATTER.format(res.getCreationTime());
×
1317
                    thisModified = DATE_TIME_FORMATTER.format(res.getLastModificationTime());
×
1318
                    thisMimeType = ((EXistResource) res).getMimeType();
×
1319
                    if (res instanceof EXistBinaryResource) {
×
1320
                        final MessageDigest messageDigest = ((EXistBinaryResource) res).getContentDigest(DigestType.BLAKE_256);
×
1321
                        thisMessageDigestType = messageDigest.getDigestType().getCommonNames()[0];
×
1322
                        thisMessageDigestValue = messageDigest.toHexString();
×
1323
                        thisSize = humanSize(((EXistBinaryResource) res).getContentLength());
×
1324
                    } else {
×
1325
                        thisMessageDigestType = NON_APPLICABLE;
×
1326
                        thisMessageDigestValue = NON_APPLICABLE;
×
1327
                        thisSize = NON_APPLICABLE;
×
1328
                    }
1329
                    thisPerm = service.getPermissions(res);
×
1330
                }
1331

1332
                name = getUpdated(name, () ->  URIUtils.urlDecodeUtf8(thisName));
×
1333
                created = getUpdated(created, thisCreated);
×
1334
                modified = getUpdated(modified, thisModified);
×
1335
                mimeType = getUpdated(mimeType, thisMimeType);
×
1336
                messageDigestType = getUpdated(messageDigestType, thisMessageDigestType);
×
1337
                messageDigestValue = getUpdated(messageDigestValue, thisMessageDigestValue);
×
1338
                size = getUpdated(size, thisSize);
×
1339
                owner = getUpdated(owner, () -> thisPerm.getOwner().getName());
×
1340
                group = getUpdated(group, () -> thisPerm.getGroup().getName());
×
1341
                mode = getUpdatedMode(mode, thisPerm);
×
1342

1343
                if (firstPerm) {
×
1344
                    if (thisPerm instanceof ACLPermission thisAcl) {
×
1345
                        acl = new SimpleACLPermissionAider();
×
1346
                        for (int i = 0; i < thisAcl.getACECount(); i++) {
×
1347
                            acl.addACE(thisAcl.getACEAccessType(i), thisAcl.getACETarget(i), thisAcl.getACEWho(i), thisAcl.getACEMode(i));
×
1348
                        }
1349
                    } else {
×
1350
                        acl = null;
×
1351
                    }
1352
                    firstPerm = false;
×
1353
                } else {
×
1354
                    if (acl != null && thisPerm instanceof ACLPermission thisAcl) {
×
1355
                        if (!acl.aclEquals(thisAcl)) {
×
1356
                            acl = null;
×
1357
                        }
1358
                    }
1359
                }
1360
            }
1361

1362
            final EditPropertiesDialog editPropertiesDialog = new EditPropertiesDialog(service, client.getProperties().getProperty(InteractiveClient.USER), collection, name, mimeType, created, modified, size, messageDigestType, messageDigestValue, owner, group, mode, acl, selected);
×
1363
            editPropertiesDialog.addWindowListener(new WindowAdapter() {
×
1364
                @Override
1365
                public void windowClosed(final WindowEvent e) {
1366
                    try {
1367
                        client.reloadCollection();
×
1368
                    } catch (final XMLDBException xmldbe) {
×
1369
                        showErrorMessage(Messages.getString("ClientFrame.197") + xmldbe.getMessage(), xmldbe); //$NON-NLS-1$
×
1370
                        xmldbe.printStackTrace();
×
1371
                    }
1372
                }
×
1373
            });
1374
            editPropertiesDialog.setVisible(true);
×
1375

1376
        } catch (final XMLDBException e) {
×
1377
            showErrorMessage(Messages.getString("ClientFrame.197") + e.getMessage(), e); //$NON-NLS-1$
×
1378
            e.printStackTrace();
×
1379
        }
1380
    }
×
1381

1382
    private String getUpdated(final String previousValue, final Supplier<String> nextValueSupplier) {
1383
        if (!MULTIPLE_INDICATOR.equals(previousValue)) {
×
1384
            final String nextValue = nextValueSupplier.get();
×
1385
            if (previousValue == null) {
×
1386
                return nextValue;
×
1387
            } else {
1388
                if (!previousValue.equals(nextValue)) {
×
1389
                    return MULTIPLE_INDICATOR;
×
1390
                }
1391
            }
1392
        }
1393

1394
        return previousValue;
×
1395
    }
1396

1397
    private String getUpdated(final String previousValue, final String nextValue) {
1398
        if (!MULTIPLE_INDICATOR.equals(previousValue)) {
×
1399
            if (previousValue == null) {
×
1400
                return nextValue;
×
1401
            } else {
1402
                if (!previousValue.equals(nextValue)) {
×
1403
                    return MULTIPLE_INDICATOR;
×
1404
                }
1405
            }
1406
        }
1407

1408
        return previousValue;
×
1409
    }
1410

1411
    private ModeDisplay getUpdatedMode(ModeDisplay previousMode, final Permission permission) {
1412
        if (previousMode == null) {
×
1413
            return ModeDisplay.fromPermission(permission);
×
1414
        }
1415

1416
        final int ownerMode = permission.getOwnerMode();
×
1417
        final boolean ownerRead = (ownerMode & Permission.READ) == Permission.READ;
×
1418
        final boolean ownerWrite = (ownerMode & Permission.WRITE) == Permission.WRITE;
×
1419
        final boolean ownerExecute = (ownerMode & Permission.EXECUTE) == Permission.EXECUTE;
×
1420

1421
        final int groupMode = permission.getGroupMode();
×
1422
        final boolean groupRead = (groupMode & Permission.READ) == Permission.READ;
×
1423
        final boolean groupWrite = (groupMode & Permission.WRITE) == Permission.WRITE;
×
1424
        final boolean groupExecute = (groupMode & Permission.EXECUTE) == Permission.EXECUTE;
×
1425

1426
        final int otherMode = permission.getOtherMode();
×
1427
        final boolean otherRead = (otherMode & Permission.READ) == Permission.READ;
×
1428
        final boolean otherWrite = (otherMode & Permission.WRITE) == Permission.WRITE;
×
1429
        final boolean otherExecute = (otherMode & Permission.EXECUTE) == Permission.EXECUTE;
×
1430

1431
        final boolean setUid = permission.isSetUid();
×
1432
        final boolean setGid = permission.isSetGid();
×
1433
        final boolean sticky = permission.isSticky();
×
1434

1435
        previousMode.ownerRead = getUpdatedModeBit(previousMode.ownerRead, ownerRead);
×
1436
        previousMode.ownerWrite = getUpdatedModeBit(previousMode.ownerWrite, ownerWrite);
×
1437
        previousMode.ownerExecute = getUpdatedModeBit(previousMode.ownerExecute, ownerExecute);
×
1438

1439
        previousMode.groupRead = getUpdatedModeBit(previousMode.groupRead, groupRead);
×
1440
        previousMode.groupWrite = getUpdatedModeBit(previousMode.groupWrite, groupWrite);
×
1441
        previousMode.groupExecute = getUpdatedModeBit(previousMode.groupExecute, groupExecute);
×
1442

1443
        previousMode.otherRead = getUpdatedModeBit(previousMode.otherRead, otherRead);
×
1444
        previousMode.otherWrite = getUpdatedModeBit(previousMode.otherWrite, otherWrite);
×
1445
        previousMode.otherExecute = getUpdatedModeBit(previousMode.otherExecute, otherExecute);
×
1446

1447
        previousMode.setUid = getUpdatedModeBit(previousMode.setUid, setUid);
×
1448
        previousMode.setGid = getUpdatedModeBit(previousMode.setGid, setGid);
×
1449
        previousMode.sticky = getUpdatedModeBit(previousMode.sticky, sticky);
×
1450

1451
        return previousMode;
×
1452
    }
1453

1454
    private Boolean getUpdatedModeBit(final Boolean previousModeBit, final boolean nextModeBit) {
1455
        if (previousModeBit == null || previousModeBit.booleanValue() != nextModeBit) {
×
1456
            return null;
×
1457
        }
1458
        return previousModeBit;
×
1459
    }
1460

1461
    private void enter() {
1462
        int end = doc.getLength();
×
1463
        if (end - commandStart == 0) {
×
1464
            return;
×
1465
        }
1466
        try {
1467
            final String command = doc.getText(commandStart, end - commandStart);
×
1468
            commandStart = end;
×
1469
            doc.insertString(commandStart++, "\n", defaultAttrs); //$NON-NLS-1$
×
1470
            if (command != null) {
×
1471
                processRunnable.setAction(command);
×
1472
                client.console.getHistory().add(command);
×
1473
            }
1474
        } catch (final BadLocationException e) {
×
1475
            e.printStackTrace();
×
1476
        }
1477
    }
×
1478

1479
    private void historyBack() {
1480
        client.console.getHistory().previous();
×
1481
        final String item = client.console.getHistory().current();
×
1482
        if (item == null) {
×
1483
            return;
×
1484
        }
1485
        try {
1486
            if (shell.getCaretPosition() > commandStart) {
×
1487
                doc.remove(commandStart, doc.getLength() - commandStart);
×
1488
            }
1489
            doc.insertString(commandStart, item, defaultAttrs);
×
1490
        } catch (final BadLocationException e) {
×
1491
            e.printStackTrace();
×
1492
        }
1493
    }
×
1494

1495
    private void historyForward() {
1496
        client.console.getHistory().next();
×
1497
        final String item = client.console.getHistory().current();
×
1498
        try {
1499
            if (shell.getCaretPosition() > commandStart) {
×
1500
                doc.remove(commandStart, doc.getLength() - commandStart);
×
1501
            }
1502
            doc.insertString(commandStart, item, defaultAttrs);
×
1503
        } catch (final BadLocationException e) {
×
1504
            e.printStackTrace();
×
1505
        }
1506
    }
×
1507

1508
    private void close() {
1509
        setVisible(false);
×
1510
        dispose();
×
1511
        processThread.interrupt();
×
1512
        System.exit(SystemExitCodes.OK_EXIT_CODE);
×
1513
    }
×
1514

1515
    private void AboutAction() {
1516
        final Icon icon = InteractiveClient.getElementalIcon(getClass());
×
1517
        JOptionPane.showMessageDialog(this, client.getNotice(), "About", INFORMATION_MESSAGE, icon);
×
1518
    }
×
1519

1520
    class TableMouseListener extends MouseAdapter {
×
1521

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

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

1552
                    ClientFrame.this.setCursor(Cursor.getDefaultCursor());
×
1553
                }
1554
            }
1555
        }
×
1556

1557
    }
1558

1559
    /**
1560
     * Compares resources according to their name, ensuring that collections
1561
     * always are before documents.
1562
     *
1563
     * @author gpothier
1564
     */
1565
    private static class ResourceComparator implements Comparator<ResourceDescriptor> {
1566
        @Override
1567
        public int compare(final ResourceDescriptor desc1, final ResourceDescriptor desc2) {
1568
            if (desc1.isCollection() != desc2.isCollection()) {
×
1569
                return desc1.isCollection() ? Constants.INFERIOR : Constants.SUPERIOR;
×
1570
            } else {
1571
                return desc1.getName().compareTo(desc2.getName());
×
1572
            }
1573
        }
1574
    }
1575

1576
    private class ProcessRunnable implements Runnable {
×
1577
        private final TransferQueue<String> queue = new LinkedTransferQueue<>();
×
1578

1579
        public void setAction(final String action) {
1580
            queue.add(action);
×
1581
        }
×
1582

1583
        @Override
1584
        public void run() {
1585
            while (true) {
1586
                final String action;
1587
                try {
1588
                    action = queue.take();
×
1589
                } catch (final InterruptedException e) {
×
1590
                    Thread.currentThread().interrupt();
×
1591
                    return;
×
1592
                }
1593

1594
                final boolean status = client.process(action);
×
1595
                displayPrompt();
×
1596

1597
                if (!status) {
×
1598
                    close();
×
1599
                    break;
1600
                }
1601
            }
1602
        }
×
1603
    }
1604

1605
    static class ResourceTableModel extends AbstractTableModel {
×
1606

1607
        private static final long serialVersionUID = 1L;
1608

1609
        private final String[] columnNames = new String[]{
×
1610
                Messages.getString("ClientFrame.207") //$NON-NLS-1$
×
1611
                , Messages.getString("ClientFrame.208") //$NON-NLS-1$
×
1612
                , Messages.getString("ClientFrame.209") //$NON-NLS-1$
×
1613
                , Messages.getString("ClientFrame.210") //$NON-NLS-1$
×
1614
                , Messages.getString("ClientFrame.211") //$NON-NLS-1$
×
1615
        };
1616

1617
        private List<ResourceDescriptor> rows = null;
×
1618

1619
        public void setData(final List<ResourceDescriptor> rows) {
1620
            rows.sort(new ResourceComparator());
×
1621
            this.rows = rows;
×
1622
            fireTableDataChanged();
×
1623
        }
×
1624

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

1629
        /*
1630
         * (non-Javadoc)
1631
         *
1632
         * @see javax.swing.table.TableModel#getColumnCount()
1633
         */
1634
        @Override
1635
        public int getColumnCount() {
1636
            return columnNames.length;
×
1637
        }
1638

1639
        /*
1640
         * (non-Javadoc)
1641
         *
1642
         * @see javax.swing.table.TableModel#getColumnName(int)
1643
         */
1644
        @Override
1645
        public String getColumnName(int column) {
1646
            return columnNames[column];
×
1647
        }
1648

1649
        /*
1650
         * (non-Javadoc)
1651
         *
1652
         * @see javax.swing.table.TableModel#getRowCount()
1653
         */
1654
        @Override
1655
        public int getRowCount() {
1656
            return rows == null ? 0 : rows.size();
×
1657
        }
1658

1659
        /*
1660
         * (non-Javadoc)
1661
         *
1662
         * @see javax.swing.table.TableModel#getValueAt(int, int)
1663
         */
1664
        @Override
1665
        public Object getValueAt(final int rowIndex, final int columnIndex) {
1666
            if (getRowCount() > 0) {
×
1667
                final ResourceDescriptor row = getRow(rowIndex);
×
1668
                //$NON-NLS-1$
1669
                return switch (columnIndex) {
×
1670
                    case 0 -> row.getName().toString();
×
1671
                    case 1 -> DATE_TIME_FORMATTER.format(row.getInstant());
×
1672
                    case 2 -> row.getOwner();
×
1673
                    case 3 -> row.getGroup();
×
1674
                    case 4 -> row.getPermissionsDescription();
×
1675
                    default -> throw new RuntimeException(Messages.getString("ClientFrame.212")); //$NON-NLS-1$
×
1676
                };
1677
            }
1678
            return "";
×
1679
        }
1680

1681
        /*
1682
         * (non-Javadoc)
1683
         *
1684
         * @see javax.swing.table.TableModel#getColumnClass(int)
1685
         */
1686
        @Override
1687
        public Class getColumnClass(final int column) {
1688
            return getValueAt(0, column).getClass();
×
1689
        }
1690
    }
1691

1692
    /**
1693
     * @param props pass properties to the login panel
1694
     * @return the modified properties
1695
     */
1696
    protected static Properties getLoginData(final Properties props) {
1697

1698
        final Properties properties = new Properties();
×
1699

1700
        final String serverUri;
1701
        if (props.getProperty(InteractiveClient.URI) == null || props.getProperty(InteractiveClient.URI).isEmpty()) {
×
1702
            serverUri = InteractiveClient.URI_DEFAULT;
×
1703
        } else {
×
1704
            if (Boolean.parseBoolean(props.getProperty(InteractiveClient.LOCAL_MODE, "FALSE"))) {
×
1705
                serverUri = InteractiveClient.URI_DEFAULT;
×
1706
            } else {
×
1707
                serverUri = props.getProperty(InteractiveClient.URI);
×
1708
            }
1709
        }
1710

1711
        final DefaultConnectionSettings defaultConnectionSettings = new DefaultConnectionSettings(
×
1712
                props.getProperty(InteractiveClient.USER, InteractiveClient.USER_DEFAULT),
×
1713
                props.getProperty(InteractiveClient.PASSWORD, ""),
×
1714
                serverUri,
×
1715
                Boolean.parseBoolean(props.getProperty(InteractiveClient.SSL_ENABLE, InteractiveClient.SSL_ENABLE_DEFAULT))
×
1716
        );
1717
        defaultConnectionSettings.setConfiguration(props.getProperty(InteractiveClient.CONFIGURATION, ""));
×
1718

1719
        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)));
×
1720

1721
        connectionDialog.setTitle(SystemProperties.getInstance().getSystemProperty("product-name", "Elemental") + " " + SystemProperties.getInstance().getSystemProperty("product-version", "unknown") + " Database Login");
×
1722

1723
        connectionDialog.addDialogCompleteWithResponseCallback(connection -> {
×
1724
            properties.setProperty(InteractiveClient.USER, connection.getUsername());
×
1725
            properties.setProperty(InteractiveClient.PASSWORD, connection.getPassword());
×
1726

1727
            if (!connection.getUri().isEmpty()) {
×
1728
                properties.setProperty(InteractiveClient.URI, connection.getUri());
×
1729
                properties.setProperty(InteractiveClient.SSL_ENABLE, Boolean.valueOf(connection.isSsl()).toString().toUpperCase());
×
1730
                properties.setProperty(InteractiveClient.LOCAL_MODE, "FALSE");
×
1731
            } else {
×
1732
                properties.setProperty(InteractiveClient.CONFIGURATION, connection.getConfiguration());
×
1733
                properties.setProperty(InteractiveClient.URI, XmldbURI.EMBEDDED_SERVER_URI.toString());
×
1734
            }
1735
        });
×
1736

1737
        connectionDialog.setVisible(true);
×
1738

1739
        return properties;
×
1740
    }
1741

1742
    public static void showErrorMessage(final String message, final Throwable t) {
1743
        JScrollPane scroll = null;
×
1744
        final JTextArea msgArea = new JTextArea(message);
×
1745
        msgArea.setBorder(BorderFactory.createTitledBorder(Messages.getString("ClientFrame.214"))); //$NON-NLS-1$
×
1746
        msgArea.setEditable(false);
×
1747
        msgArea.setBackground(null);
×
1748
        if (t != null) {
×
1749
            final StringWriter out = new StringWriter();
×
1750
            final PrintWriter writer = new PrintWriter(out);
×
1751
            t.printStackTrace(writer);
×
1752
            final JTextArea stacktrace = new JTextArea(out.toString(), 20, 50);
×
1753
            stacktrace.setBackground(null);
×
1754
            stacktrace.setEditable(false);
×
1755
            scroll = new JScrollPane(stacktrace);
×
1756
            scroll.setPreferredSize(new Dimension(250, 300));
×
1757
            scroll.setBorder(BorderFactory
×
1758
                    .createTitledBorder(Messages.getString("ClientFrame.215"))); //$NON-NLS-1$
×
1759
        }
1760
        final JOptionPane optionPane = new JOptionPane();
×
1761
        optionPane.setMessage(new Object[]{msgArea, scroll});
×
1762
        optionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
×
1763
        final JDialog dialog = optionPane.createDialog(null, Messages.getString("ClientFrame.216")); //$NON-NLS-1$
×
1764
        dialog.setResizable(true);
×
1765
        dialog.pack();
×
1766
        dialog.setVisible(true);
×
1767
    }
×
1768

1769
    public static int showErrorMessageQuery(final String message, final Throwable t) {
1770
        final JTextArea msgArea = new JTextArea(message);
×
1771
        msgArea.setLineWrap(true);
×
1772
        msgArea.setWrapStyleWord(true);
×
1773
        msgArea.setEditable(false);
×
1774
        msgArea.setBackground(null);
×
1775
        JScrollPane scrollMsgArea = new JScrollPane(msgArea);
×
1776
        scrollMsgArea.setPreferredSize(new Dimension(600, 300));
×
1777
        scrollMsgArea.setBorder(BorderFactory
×
1778
                .createTitledBorder(Messages.getString("ClientFrame.217"))); //$NON-NLS-1$
×
1779

1780
        JScrollPane scrollStacktrace = null;
×
1781
        if (t != null) {
×
1782
            try (final StringWriter out = new StringWriter();
×
1783
                 final PrintWriter writer = new PrintWriter(out)) {
×
1784
                t.printStackTrace(writer);
×
1785
                final JTextArea stacktrace = new JTextArea(out.toString(), 20, 50);
×
1786
                stacktrace.setLineWrap(true);
×
1787
                stacktrace.setWrapStyleWord(true);
×
1788
                stacktrace.setBackground(null);
×
1789
                stacktrace.setEditable(false);
×
1790
                scrollStacktrace = new JScrollPane(stacktrace);
×
1791
                scrollStacktrace.setPreferredSize(new Dimension(600, 300));
×
1792
                scrollStacktrace.setBorder(BorderFactory
×
1793
                        .createTitledBorder(Messages.getString("ClientFrame.218"))); //$NON-NLS-1$
×
1794
            } catch (final IOException ioe) {
×
1795
                ioe.printStackTrace();
×
1796
            }
1797
        }
1798

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

1808
        final Object result = optionPane.getValue();
×
1809
        if (result == null) {
×
1810
            return 2;
×
1811
        }
1812
        return (Integer) optionPane.getValue();
×
1813
    }
1814

1815
    /*
1816
     * (non-Javadoc)
1817
     *
1818
     * @see java.awt.event.WindowFocusListener#windowGainedFocus(java.awt.event.WindowEvent)
1819
     */
1820
    @Override
1821
    public void windowGainedFocus(final WindowEvent e) {
1822
        toFront();
×
1823
    }
×
1824

1825
    /*
1826
     * (non-Javadoc)
1827
     *
1828
     * @see java.awt.event.WindowFocusListener#windowLostFocus(java.awt.event.WindowEvent)
1829
     */
1830
    @Override
1831
    public void windowLostFocus(final WindowEvent e) {
1832
    }
×
1833

1834
    /*
1835
     * (non-Javadoc)
1836
     *
1837
     * @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
1838
     */
1839
    @Override
1840
    public void mouseClicked(final MouseEvent e) {
1841
    }
×
1842

1843
    /*
1844
     * (non-Javadoc)
1845
     *
1846
     * @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
1847
     */
1848
    @Override
1849
    public void mouseEntered(final MouseEvent e) {
1850
    }
×
1851

1852
    /*
1853
     * (non-Javadoc)
1854
     *
1855
     * @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
1856
     */
1857
    @Override
1858
    public void mouseExited(final MouseEvent e) {
1859
    }
×
1860

1861
    /*
1862
     * (non-Javadoc)
1863
     *
1864
     * @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
1865
     */
1866
    @Override
1867
    public void mousePressed(final MouseEvent e) {
1868
        if (e.isPopupTrigger()) {
×
1869
            shellPopup.show((Component) e.getSource(), e.getX(), e.getY());
×
1870
        }
1871
    }
×
1872

1873
    /*
1874
     * (non-Javadoc)
1875
     *
1876
     * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
1877
     */
1878
    @Override
1879
    public void mouseReleased(final MouseEvent e) {
1880
        if (e.isPopupTrigger()) {
×
1881
            shellPopup.show((Component) e.getSource(), e.getX(), e.getY());
×
1882
        }
1883
    }
×
1884

1885
    static class BinaryFileFilter extends FileFilter {
×
1886

1887
        /* (non-Javadoc)
1888
         * @see javax.swing.filechooser.FileFilter#getDescription()
1889
         */
1890
        @Override
1891
        public String getDescription() {
1892
            return Messages.getString("ClientFrame.220"); //$NON-NLS-1$
×
1893
        }
1894

1895
        /* (non-Javadoc)
1896
         * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
1897
         */
1898
        @Override
1899
        public boolean accept(final File f) {
1900
            if (f.isDirectory()) {
×
1901
                return true;
×
1902
            }
1903
            return !MimeTable.getInstance().isXMLContent(f.getName());
×
1904
        }
1905
    }
1906

1907
    static class XMLFileFilter extends FileFilter {
×
1908

1909
        /* (non-Javadoc)
1910
         * @see javax.swing.filechooser.FileFilter#getDescription()
1911
         */
1912
        @Override
1913
        public String getDescription() {
1914
            return Messages.getString("ClientFrame.221"); //$NON-NLS-1$
×
1915
        }
1916

1917
        /* (non-Javadoc)
1918
         * @see javax.swing.filechooser.FileFilter#accept(java.io.File)
1919
         */
1920
        @Override
1921
        public boolean accept(final File f) {
1922
            if (f.isDirectory()) {
×
1923
                return true;
×
1924
            }
1925
            return MimeTable.getInstance().isXMLContent(f.getName());
×
1926
        }
1927
    }
1928

1929
    private class FileListDropTargetListener implements DropTargetListener {
×
1930

1931
        @Override
1932
        public void dragEnter(final DropTargetDragEvent dtde) {
1933
        }
×
1934

1935
        @Override
1936
        public void dragOver(final DropTargetDragEvent dtde) {
1937
        }
×
1938

1939
        @Override
1940
        public void dropActionChanged(final DropTargetDragEvent dtde) {
1941
        }
×
1942

1943
        @Override
1944
        public void dragExit(final DropTargetEvent dte) {
1945

1946
        }
×
1947

1948
        @Override
1949
        public void drop(final DropTargetDropEvent dtde) {
1950
            try {
1951

1952
                dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
×
1953

1954
                final Transferable transferable = dtde.getTransferable();
×
1955

1956
                //should work for Win32 systems
1957
                List<Path> files = getFilesWin32(transferable);
×
1958

1959
                //should work for *nix systems
1960
                if (files == null) {
×
1961
                    files = getFilesUnix(transferable);
×
1962
                }
1963

1964
                if (files != null) {
×
1965
                    uploadFiles(files);
×
1966
                }
1967
            } catch (final URISyntaxException | IOException | UnsupportedFlavorException | ClassNotFoundException use) {
×
1968
                System.err.println("An exception occurred while dragging and dropping files: " + use.getMessage());
×
1969
                use.printStackTrace();
×
1970
            }
1971
        }
×
1972

1973
        private List<Path> getFilesWin32(final Transferable transferable) throws UnsupportedFlavorException, IOException {
1974
            return ((List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor)).stream().map(File::toPath).collect(Collectors.toList());
×
1975
        }
1976

1977
        private List<Path> getFilesUnix(final Transferable transferable) throws ClassNotFoundException, UnsupportedFlavorException, IOException, URISyntaxException {
1978

1979
            List<Path> files = null;
×
1980
            final DataFlavor unixFileDataFlavour = new DataFlavor("text/uri-list;class=java.lang.String");
×
1981
            final String data = (String) transferable.getTransferData(unixFileDataFlavour);
×
1982
            for (final StringTokenizer st = new StringTokenizer(data, "\r\n"); st.hasMoreTokens(); ) {
×
1983
                final String token = st.nextToken().trim();
×
1984
                if (token.startsWith("#") || token.isEmpty()) {
×
1985
                    // comment line, by RFC 2483
1986
                    continue;
×
1987
                }
1988

1989
                //lazy
1990
                if (files == null) {
×
1991
                    files = new ArrayList<>();
×
1992
                }
1993

1994
                files.add(Paths.get(new URI(token)));
×
1995
            }
1996

1997
            return files;
×
1998
        }
1999
    }
2000
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc