• 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.0
/exist-core/src/main/java/org/exist/client/QueryDialog.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 java.awt.BorderLayout;
52
import java.awt.Cursor;
53
import java.awt.Dimension;
54
import java.awt.event.ItemEvent;
55
import java.awt.event.WindowAdapter;
56
import java.awt.event.WindowEvent;
57
import java.io.*;
58
import java.net.URL;
59
import java.nio.file.Files;
60
import java.nio.file.Path;
61
import java.nio.file.Paths;
62
import java.util.ArrayList;
63
import java.util.List;
64
import java.util.Properties;
65
import java.util.concurrent.atomic.AtomicInteger;
66
import java.util.concurrent.atomic.AtomicReference;
67
import javax.swing.BorderFactory;
68
import javax.swing.Box;
69
import javax.swing.DefaultComboBoxModel;
70
import javax.swing.ImageIcon;
71
import javax.swing.JButton;
72
import javax.swing.JComboBox;
73
import javax.swing.JComponent;
74
import javax.swing.JFileChooser;
75
import javax.swing.JFrame;
76
import javax.swing.JLabel;
77
import javax.swing.JOptionPane;
78
import javax.swing.JPanel;
79
import javax.swing.JProgressBar;
80
import javax.swing.JSpinner;
81
import javax.swing.JSplitPane;
82
import javax.swing.JTabbedPane;
83
import javax.swing.JTextField;
84
import javax.swing.JToolBar;
85
import javax.swing.SpinnerNumberModel;
86
import javax.swing.SwingWorker;
87
import javax.swing.border.BevelBorder;
88
import javax.swing.event.PopupMenuEvent;
89
import javax.swing.event.PopupMenuListener;
90
import javax.xml.transform.OutputKeys;
91

92
import org.exist.security.PermissionDeniedException;
93
import org.exist.util.Holder;
94
import org.exist.xmldb.EXistXQueryService;
95
import org.exist.xmldb.LocalCollection;
96
import org.exist.xmldb.UserManagementService;
97
import org.exist.xmldb.XmldbURI;
98
import org.exist.xquery.CompiledXQuery;
99
import org.exist.xquery.XQueryContext;
100
import org.exist.xquery.XQueryWatchDog;
101
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
102
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
103
import org.fife.ui.rtextarea.RTextScrollPane;
104
import org.xmldb.api.base.Collection;
105
import org.xmldb.api.base.CompiledExpression;
106
import org.xmldb.api.base.ErrorCodes;
107
import org.xmldb.api.base.Resource;
108
import org.xmldb.api.base.ResourceIterator;
109
import org.xmldb.api.base.ResourceSet;
110
import org.xmldb.api.base.XMLDBException;
111

112
import static java.nio.charset.StandardCharsets.UTF_8;
113
import static org.xmldb.api.base.ResourceType.XML_RESOURCE;
114

115
/**
116
 * @author <a href="mailto:adam@evolvedbinary.com">Adam Retter</a>
117
 */
118
public class QueryDialog extends JFrame {
119
    private static final long serialVersionUID = 1L;
120

121
    private static final String LOADING_INDICATOR = "Loading...";
122
    private static final String WORKING_DIR = "working-dir";
123
    private static final String QUERY_DIALOG_ERROR = "QueryDialog.Error";
124
    private static final String QUERY_DIALOG_COMPILATION = "QueryDialog.Compilation";
125
    private static final String QUERY_DIALOG_EXECUTION = "QueryDialog.Execution";
126

127
    private static final AtomicInteger QUERY_THREAD_ID = new AtomicInteger();
×
128

129
    private InteractiveClient client;
130
    private Collection collection;
131
    private Properties properties;
132
    private RSyntaxTextArea query;
133
    private JTabbedPane resultTabs;
134
    private RSyntaxTextArea resultDisplay;
135
    private RTextScrollPane resultDisplayScrollPane;
136
    private RSyntaxTextArea exprDisplay;
137
    private RTextScrollPane exprDisplayScrollPane;
138
    private JComboBox<String> collections = null;
×
139
    private SpinnerNumberModel count;
140
    private DefaultComboBoxModel<String> history = new DefaultComboBoxModel<>();
×
141
    private JTextField statusMessage;
142
    private JProgressBar progress;
143
    private JButton submitButton;
144
    private JButton killButton;
145
    private QueryRunnable queryRunnable = null;
×
146
    private Resource resource = null;
×
147

148
    private QueryDialog(final InteractiveClient client, final Collection collection, final Properties properties, boolean loadedFromDb) {
149
        super(Messages.getString("QueryDialog.0"));
×
150
        this.collection = collection;
×
151
        this.properties = properties;
×
152
        this.client = client;
×
153
        this.setIconImage(InteractiveClient.getElementalIcon(getClass()).getImage());
×
154
        setupComponents(loadedFromDb);
×
155
        pack();
×
156
    }
×
157

158
    public QueryDialog(final InteractiveClient client, final Collection collection, final Properties properties) {
159
        this(client, collection, properties, false);
×
160
    }
×
161

162
    public QueryDialog(final InteractiveClient client, final Collection collection, final Resource resource, final Properties properties) throws XMLDBException {
163
        this(client, collection, properties, true);
×
164
        this.resource = resource;
×
165
        addWindowListener(new WindowAdapter() {
×
166
            @Override
167
            public void windowClosing(WindowEvent ev) {
168
                try {
169
                    final UserManagementService service = collection.getService(UserManagementService.class);
×
170
                    service.unlockResource(resource);
×
171
                } catch (final XMLDBException e) {
×
172
                    e.printStackTrace();
×
173
                }
174
            }
×
175
        });
176

177
        //set the content of the query
178
        query.setText(new String((byte[]) resource.getContent()));
×
179

180
        //set title
181
        setTitle(Messages.getString("QueryDialog.0") + ": " + resource.getId());
×
182
    }
×
183

184
    private void saveToDb(final String queryText) {
185

186
        try {
187
            resource.setContent(queryText);
×
188
            collection.storeResource(resource);
×
189
        } catch (final XMLDBException xmldbe) {
×
190
            ClientFrame.showErrorMessage(xmldbe.getMessage(), xmldbe);
×
191
        }
192
    }
×
193

194
    private void setupComponents(boolean loadedFromDb) {
195
        getContentPane().setLayout(new BorderLayout());
×
196
        final JToolBar toolbar = new JToolBar();
×
197

198
        URL url = getClass().getResource("icons/Open24.gif");
×
199
        JButton button = new JButton(new ImageIcon(url));
×
200
        button.setToolTipText(Messages.getString("QueryDialog.opentooltip"));
×
201
        button.addActionListener(e -> open());
×
202
        toolbar.add(button);
×
203

204
        if (loadedFromDb) {
×
205
            url = getClass().getResource("icons/SaveAs23.gif");
×
206
            button = new JButton(new ImageIcon(url));
×
207
            button.setToolTipText("Save to database");
×
208
            button.addActionListener(e -> saveToDb(query.getText()));
×
209
            toolbar.add(button);
×
210
        }
211

212
        url = getClass().getResource("icons/SaveAs24.gif");
×
213
        button = new JButton(new ImageIcon(url));
×
214
        button.setToolTipText(Messages.getString("QueryDialog.saveastooltip"));
×
215
        button.addActionListener(e -> save(query.getText(), "query"));
×
216
        toolbar.add(button);
×
217

218
        url = getClass().getResource("icons/SaveAs25.gif");
×
219
        button = new JButton(new ImageIcon(url));
×
220
        button.setToolTipText(Messages.getString("QueryDialog.saveresultstooltip"));
×
221
        button.addActionListener(e -> save(resultDisplay.getText(), "result"));
×
222
        toolbar.add(button);
×
223

224
        toolbar.addSeparator();
×
225
        url = getClass().getResource("icons/Copy24.gif");
×
226
        button = new JButton(new ImageIcon(url));
×
227
        button.setToolTipText(Messages.getString("QueryDialog.copytooltip"));
×
228
        button.addActionListener(e -> query.copy());
×
229
        toolbar.add(button);
×
230
        url = getClass().getResource("icons/Cut24.gif");
×
231
        button = new JButton(new ImageIcon(url));
×
232
        button.setToolTipText(Messages.getString("QueryDialog.cuttooltip"));
×
233
        button.addActionListener(e -> query.cut());
×
234
        toolbar.add(button);
×
235
        url = getClass().getResource("icons/Paste24.gif");
×
236
        button = new JButton(new ImageIcon(url));
×
237
        button.setToolTipText(Messages.getString("QueryDialog.pastetooltip"));
×
238
        button.addActionListener(e -> query.paste());
×
239
        toolbar.add(button);
×
240

241
        toolbar.addSeparator();
×
242
        //TODO: change icon
243
        url = getClass().getResource("icons/Find24.gif");
×
244
        button = new JButton(new ImageIcon(url));
×
245
        button.setToolTipText(Messages.getString("QueryDialog.compiletooltip"));
×
246
        button.addActionListener(e -> compileQuery());
×
247
        toolbar.add(button);
×
248

249
        toolbar.addSeparator();
×
250
        url = getClass().getResource("icons/Find24.gif");
×
251
        submitButton = new JButton(Messages.getString("QueryDialog.submitbutton"), new ImageIcon(url));
×
252
        submitButton.setToolTipText(Messages.getString("QueryDialog.submittooltip"));
×
253
        toolbar.add(submitButton);
×
254
        submitButton.addActionListener(e -> {
×
255
            submitButton.setEnabled(false);
×
256
            if (collection instanceof LocalCollection) {
×
257
                killButton.setEnabled(true);
×
258
            }
259
            queryRunnable = doQuery();
×
260
        });
×
261

262
        toolbar.addSeparator();
×
263
        url = getClass().getResource("icons/Delete24.gif");
×
264
        killButton = new JButton(Messages.getString("QueryDialog.killbutton"), new ImageIcon(url));
×
265
        killButton.setToolTipText(Messages.getString("QueryDialog.killtooltip"));
×
266
        toolbar.add(killButton);
×
267
        killButton.setEnabled(false);
×
268
        killButton.addActionListener(e -> {
×
269
            if (queryRunnable != null) {
×
270
                queryRunnable.killQuery();
×
271
                killButton.setEnabled(false);
×
272

273
                queryRunnable = null;
×
274
            }
275
        });
×
276

277
        final JSplitPane split = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
×
278
        split.setResizeWeight(0.6);
×
279

280
        final JComponent qbox = createQueryBox();
×
281
        split.setTopComponent(qbox);
×
282

283
        final JPanel vbox = new JPanel();
×
284
        vbox.setLayout(new BorderLayout());
×
285

286
        final JLabel label = new JLabel(Messages.getString("QueryDialog.resultslabel"));
×
287
        vbox.add(label, BorderLayout.NORTH);
×
288

289
        resultTabs = new JTabbedPane();
×
290

291
        resultDisplay = new RSyntaxTextArea(14, 80);
×
292
        resultDisplay.setEditable(false);
×
293
        resultDisplay.getPopupMenu().remove(0); //remove undo
×
294
        resultDisplay.getPopupMenu().remove(0); //remove redo
×
295
        resultDisplay.getPopupMenu().remove(0); //remove undo/redo splitter
×
296
        resultDisplay.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
×
297
        resultDisplay.setCodeFoldingEnabled(true);
×
298
        resultDisplayScrollPane = new RTextScrollPane(resultDisplay);
×
299

300
        resultTabs.add(Messages.getString("QueryDialog.XMLtab"), resultDisplayScrollPane);
×
301

302
        exprDisplay = new RSyntaxTextArea(14, 80);
×
303
        exprDisplay.setEditable(false);
×
304
        exprDisplay.getPopupMenu().remove(0); //remove undo
×
305
        exprDisplay.getPopupMenu().remove(0); //remove redo
×
306
        exprDisplay.getPopupMenu().remove(0); //remove undo/redo splitter
×
307
        exprDisplayScrollPane = new RTextScrollPane(exprDisplay);
×
308
        resultTabs.add(Messages.getString("QueryDialog.tracetab"), exprDisplayScrollPane);
×
309

310
        vbox.add(resultTabs, BorderLayout.CENTER);
×
311

312
        final Box statusbar = Box.createHorizontalBox();
×
313
        statusbar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
×
314
        statusMessage = new JTextField(20);
×
315
        statusMessage.setEditable(false);
×
316
        statusMessage.setFocusable(true);
×
317
        statusbar.add(statusMessage);
×
318
        JTextField queryPositionDisplay = new JTextField(5);
×
319
        queryPositionDisplay.setEditable(false);
×
320
        queryPositionDisplay.setFocusable(true);
×
321
        statusbar.add(queryPositionDisplay);
×
322
        query.addCaretListener(e -> {
×
323
            final RSyntaxTextArea txt = (RSyntaxTextArea) e.getSource();
×
324
            queryPositionDisplay.setText("Line: " + (txt.getCaretLineNumber() + 1) + " Column:" + (txt.getCaretOffsetFromLineStart() + 1));
×
325
        });
×
326

327
        progress = new JProgressBar();
×
328
        progress.setPreferredSize(new Dimension(200, statusbar.getHeight()));
×
329
        progress.setVisible(false);
×
330
        statusbar.add(progress);
×
331

332
        vbox.add(statusbar, BorderLayout.SOUTH);
×
333

334
        split.setBottomComponent(vbox);
×
335
        split.setDividerLocation(0.6);
×
336
        getContentPane().add(toolbar, BorderLayout.NORTH);
×
337
        getContentPane().add(split, BorderLayout.CENTER);
×
338
    }
×
339

340
    private JComponent createQueryBox() {
341
        final JTabbedPane tabs = new JTabbedPane();
×
342

343
        final JPanel inputVBox = new JPanel();
×
344
        inputVBox.setLayout(new BorderLayout());
×
345
        tabs.add(Messages.getString("QueryDialog.inputtab"), inputVBox);
×
346

347
        final Box historyBox = Box.createHorizontalBox();
×
348
        JLabel label = new JLabel(Messages.getString("QueryDialog.historylabel"));
×
349
        historyBox.add(label);
×
350
        final JComboBox<String> historyList = new JComboBox<>(history);
×
351
        for (final String queryHistory : client.queryHistory) {
×
352
            addQuery(queryHistory);
×
353
        }
354
        historyList.setSelectedIndex(-1);  // by default - we are not using anything from the history!
×
355
        historyList.addItemListener(itemEvent -> {
×
356
            if (itemEvent.getStateChange() == ItemEvent.SELECTED) {
×
357
                final JComboBox<String> list = (JComboBox<String>) itemEvent.getSource();
×
358
                final int idx = list.getSelectedIndex();
×
359
                query.setText(client.queryHistory.get(idx));
×
360
            }
361
        });
×
362
        historyBox.add(historyList);
×
363
        inputVBox.add(historyBox, BorderLayout.NORTH);
×
364

365
        query = new RSyntaxTextArea(14, 80);
×
366
        query.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
×
367
        query.setCodeFoldingEnabled(true);
×
368
        RTextScrollPane queryScrollPane = new RTextScrollPane(query);
×
369
        inputVBox.add(queryScrollPane, BorderLayout.CENTER);
×
370

371
        final Box optionsPanel = Box.createHorizontalBox();
×
372

373
        label = new JLabel(Messages.getString("QueryDialog.contextlabel"));
×
374
        optionsPanel.add(label);
×
375

376
        final Holder<Boolean> addedLoadingIndicator = new Holder<>(false);
×
377
        final List<String> collectionsList = new ArrayList<>();
×
378
        try {
379
            final String currentCollectionName = collection.getName();
×
380

381
            collectionsList.add(currentCollectionName);
×
382

383
            collections = new JComboBox<>(new java.util.Vector<>(collectionsList));
×
384

385
            collections.addPopupMenuListener(new PopupMenuListener() {
×
386
                @Override
387
                public void popupMenuWillBecomeVisible(final PopupMenuEvent e) {
388
                    if (!addedLoadingIndicator.value) {
×
389
                        collections.addItem(LOADING_INDICATOR);
×
390
                        addedLoadingIndicator.value = true;
×
391

392
                        final GetCollectionsListSwingWorker getCollectionsListSwingWorker = new GetCollectionsListSwingWorker(currentCollectionName);
×
393
                        getCollectionsListSwingWorker.execute();
×
394
                    }
395
                }
×
396

397
                @Override
398
                public void popupMenuWillBecomeInvisible(final PopupMenuEvent e) {
399
                    // no action
400
                }
×
401

402
                @Override
403
                public void popupMenuCanceled(final PopupMenuEvent e) {
404
                    // no action
405
                }
×
406
            });
407
        } catch (final XMLDBException e) {
×
408
            ClientFrame.showErrorMessage(
×
409
                    Messages.getString("QueryDialog.collectionretrievalerrormessage") + ".", e);
×
410
        }
411

412
        collections.addActionListener(e -> {
×
413
            final int p = collections.getSelectedIndex();
×
414
            final String context;
415
            if (p == -1) {
×
416
                context = "/db";
×
417
            } else {
×
418
                context = collectionsList.get(p);
×
419
            }
420
            try {
421
                collection = client.getCollection(context);
×
422
            } catch (final XMLDBException e1) {
×
423
                // ignore error
424
            }
425
        });
×
426
        optionsPanel.add(collections);
×
427

428
        label = new JLabel(Messages.getString("QueryDialog.maxlabel"));
×
429
        optionsPanel.add(label);
×
430

431
        count = new SpinnerNumberModel(100, 1, 10000, 50);
×
432
        final JSpinner spinner = new JSpinner(count);
×
433
        spinner.setMaximumSize(new Dimension(400, 100));
×
434
        optionsPanel.add(spinner);
×
435

436
        inputVBox.add(optionsPanel, BorderLayout.SOUTH);
×
437
        return tabs;
×
438
    }
439

440
    private void open() {
441
        final String workDir = properties.getProperty(WORKING_DIR, System.getProperty("user.dir"));
×
442
        final JFileChooser chooser = new JFileChooser();
×
443
        chooser.setCurrentDirectory(Paths.get(workDir).toFile());
×
444
        chooser.setMultiSelectionEnabled(false);
×
445
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
×
446
        chooser.addChoosableFileFilter(new MimeTypeFileFilter("application/xquery"));
×
447

448
        if (chooser.showDialog(this, Messages.getString("QueryDialog.opendialog")) == JFileChooser.APPROVE_OPTION) {
×
449
            final Path selectedDir = chooser.getCurrentDirectory().toPath();
×
450
            properties.setProperty(WORKING_DIR, selectedDir.toAbsolutePath().toString());
×
451
            final Path file = chooser.getSelectedFile().toPath();
×
452
            if (!Files.isReadable(file)) {
×
453
                JOptionPane.showInternalMessageDialog(this, Messages.getString("QueryDialog.cannotreadmessage") + " " + file.toAbsolutePath(),
×
454
                        Messages.getString(QUERY_DIALOG_ERROR), JOptionPane.ERROR_MESSAGE);
×
455
            }
456

457
            try (final BufferedReader reader = Files.newBufferedReader(file)) {
×
458
                final StringBuilder buf = new StringBuilder();
×
459
                String line;
460
                while ((line = reader.readLine()) != null) {
×
461
                    buf.append(line);
×
462
                    buf.append('\n');
×
463
                }
464
                query.setText(buf.toString());
×
465

466
            } catch (final IOException e) {
×
467
                ClientFrame.showErrorMessage(e.getMessage(), e);
×
468
            }
469
        }
470
    }
×
471

472
    private void save(String stringToSave, String fileCategory) {
473
        if (stringToSave == null || stringToSave.isEmpty()) {
×
474
            return;
×
475
        }
476
        final String workDir = properties.getProperty(WORKING_DIR, System.getProperty("user.dir"));
×
477
        final JFileChooser chooser = new JFileChooser();
×
478
        chooser.setMultiSelectionEnabled(false);
×
479
        chooser.setCurrentDirectory(Paths.get(workDir).toFile());
×
480
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
×
481
        if ("result".equals(fileCategory)) {
×
482
            chooser.addChoosableFileFilter(new MimeTypeFileFilter("application/xhtml+xml"));
×
483
            chooser.addChoosableFileFilter(new MimeTypeFileFilter("application/xml"));
×
484
        } else {
×
485
            chooser.addChoosableFileFilter(new MimeTypeFileFilter("application/xquery"));
×
486
        }
487
        if (chooser.showDialog(this, Messages.getString("QueryDialog.savedialogpre") + " " + fileCategory + " " + Messages.getString("QueryDialog.savedialogpost"))
×
488
                == JFileChooser.APPROVE_OPTION) {
489
            final Path selectedDir = chooser.getCurrentDirectory().toPath();
×
490
            properties.setProperty(WORKING_DIR, selectedDir.toAbsolutePath().toString());
×
491
            final Path file = chooser.getSelectedFile().toPath();
×
492
            if (Files.exists(file) && (!Files.isWritable(file))) {
×
493
                JOptionPane.showMessageDialog(this, Messages.getString("QueryDialog.cannotsavemessagepre") + " " + fileCategory + " " + Messages.getString("QueryDialog.cannotsavemessageinf") + " " + file.toAbsolutePath(),
×
494
                        Messages.getString(QUERY_DIALOG_ERROR), JOptionPane.ERROR_MESSAGE);
×
495
            }
496
            if (Files.exists(file) &&
×
497
                    JOptionPane.showConfirmDialog(this, Messages.getString("QueryDialog.savedialogconfirm"), "Overwrite?",
×
498
                            JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION) {
×
499
                return;
×
500
            }
501
            try (final Writer writer = Files.newBufferedWriter(file)) {
×
502
                writer.write(stringToSave);
×
503
            } catch (final IOException e) {
×
504
                ClientFrame.showErrorMessage(e.getMessage(), e);
×
505
            }
506
        }
507
    }
×
508

509
    private QueryRunnable doQuery() {
510
        final String xpath = query.getText();
×
511
        if (xpath.length() == 0) {
×
512
            return null;
×
513
        }
514
        resultDisplay.setText("");
×
515

516
        final QueryRunnable queryTask = new QueryRunnable(xpath);
×
517
        final Thread queryThread = client.newClientThread("query-" + QUERY_THREAD_ID.getAndIncrement(), queryTask);
×
518
        queryThread.start();
×
519
        return queryTask;
×
520
    }
521

522

523
    private void compileQuery() {
524
        final String xpath = query.getText();
×
525
        if (xpath.length() == 0) {
×
526
            return;
×
527
        }
528
        resultDisplay.setText("");
×
529

530
        statusMessage.setText(Messages.getString("QueryDialog.compilemessage"));
×
531
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
×
532
        final long tResult = 0;
×
533
        long tCompiled = 0;
×
534

535
        try {
536
            final EXistXQueryService service = collection.getService(EXistXQueryService.class);
×
537
            service.setProperty(OutputKeys.INDENT, properties.getProperty(OutputKeys.INDENT, "yes"));
×
538
            final long t0 = System.currentTimeMillis();
×
539
            final CompiledExpression compiled = service.compile(xpath);
×
540
            final long t1 = System.currentTimeMillis();
×
541
            tCompiled = t1 - t0;
×
542

543
            // In this way we can see the parsed structure meanwhile the query is
544
            final StringWriter writer = new StringWriter();
×
545
            service.dump(compiled, writer);
×
546
            exprDisplay.setText(writer.toString());
×
547
            resultTabs.setSelectedComponent(exprDisplayScrollPane);
×
548

549
            statusMessage.setText(Messages.getString(QUERY_DIALOG_COMPILATION) + ": " + tCompiled + "ms");
×
550

551
        } catch (final Throwable e) {
×
552
            statusMessage.setText(Messages.getString(QUERY_DIALOG_ERROR) + ": " + InteractiveClient.getExceptionMessage(e) + ". " + Messages.getString(QUERY_DIALOG_COMPILATION) + ": " + tCompiled + "ms, " + Messages.getString(QUERY_DIALOG_EXECUTION) + ": " + tResult + "ms");
×
553

554
            ClientFrame.showErrorMessageQuery(
×
555
                    Messages.getString("QueryDialog.compilationerrormessage") + ": "
×
556
                            + InteractiveClient.getExceptionMessage(e), e);
×
557

558
        }
559

560
        setCursor(Cursor.getDefaultCursor());
×
561
    }
×
562

563
    private class QueryRunnable implements Runnable {
564
        private final String xpath;
565
        private final AtomicReference<XQueryContext> runningContext = new AtomicReference<>();
×
566

567
        public QueryRunnable(final String query) {
×
568
            this.xpath = query;
×
569
        }
×
570

571
        public boolean killQuery() {
572
            final XQueryContext contextRef = runningContext.get();
×
573
            if (contextRef != null) {
×
574
                final XQueryWatchDog xwd = contextRef.getWatchDog();
×
575
                final boolean retval = !xwd.isTerminating();
×
576
                if (retval) {
×
577
                    xwd.kill(0);
×
578
                }
579
                runningContext.compareAndSet(contextRef, null);
×
580

581
                return retval;
×
582
            }
583

584
            return false;
×
585
        }
586

587
        @Override
588
        public void run() {
589
            statusMessage.setText(Messages.getString("QueryDialog.processingquerymessage"));
×
590
            progress.setVisible(true);
×
591
            progress.setIndeterminate(true);
×
592
            setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
×
593
            long tResult = 0;
×
594
            long tCompiled = 0;
×
595
            ResourceSet result = null;
×
596
            XQueryContext context = null;
×
597
            try {
598
                final EXistXQueryService service = collection.getService(EXistXQueryService.class);
×
599
                service.setProperty(OutputKeys.INDENT, properties.getProperty(OutputKeys.INDENT, "yes"));
×
600
                final long t0 = System.currentTimeMillis();
×
601

602
                if (resource != null) {
×
603
                    service.setModuleLoadPath(XmldbURI.EMBEDDED_SERVER_URI_PREFIX + resource.getParentCollection().getName());
×
604
                }
605

606
                final CompiledExpression compiled = service.compile(xpath);
×
607
                final long t1 = System.currentTimeMillis();
×
608
                // Check could also be collection instanceof LocalCollection
609
                if (compiled instanceof CompiledXQuery) {
×
610
                    context = ((CompiledXQuery) compiled).getContext();
×
611
                    runningContext.set(context);
×
612
                }
613
                tCompiled = t1 - t0;
×
614

615
                // In this way we can see the parsed structure meanwhile the query is
616
                StringWriter writer = new StringWriter();
×
617
                service.dump(compiled, writer);
×
618
                exprDisplay.setText(writer.toString());
×
619

620
                result = service.execute(compiled);
×
621
                tResult = System.currentTimeMillis() - t1;
×
622
                runningContext.set(null);
×
623

624
                if (result == null) {
×
625
                    throw new XMLDBException(ErrorCodes.UNKNOWN_ERROR, "Query returned 'null' which it should never do, this is likely a bug that should be reported");
×
626
                }
627

628
                // jmfg: Is this still needed? I don't think so
629
                writer = new StringWriter();
×
630
                service.dump(compiled, writer);
×
631
                exprDisplay.setText(writer.toString());
×
632

633
                statusMessage.setText(Messages.getString("QueryDialog.retrievingmessage"));
×
634
                final int howmany = count.getNumber().intValue();
×
635
                progress.setIndeterminate(false);
×
636
                progress.setMinimum(1);
×
637
                progress.setMaximum(howmany);
×
638
                int j = 0;
×
639
                int select = -1;
×
640
                final StringBuilder contents = new StringBuilder();
×
641
                for (final ResourceIterator i = result.getIterator(); i.hasMoreResources() && j < howmany; j++) {
×
642
                    try (Resource processedResource = i.nextResource()) {
×
643
                        progress.setValue(j);
×
644
                        if (XML_RESOURCE.equals(processedResource.getResourceType())) {
×
645
                            contents.append((String) processedResource.getContent());
×
646
                        } else {
×
647
                            contents.append(new String((byte[]) processedResource.getContent(), UTF_8));
×
648
                        }
649
                        contents.append("\n");
×
650
                    } catch (final XMLDBException e) {
×
651
                        select = ClientFrame.showErrorMessageQuery(
×
652
                                Messages.getString("QueryDialog.retrievalerrormessage") + ": "
×
653
                                        + InteractiveClient.getExceptionMessage(e), e);
×
654
                        if (select == 3) {
×
655
                            break;
×
656
                        }
657
                    }
658
                }
659
                resultTabs.setSelectedComponent(resultDisplayScrollPane);
×
660
                resultDisplay.setText(contents.toString());
×
661
                resultDisplay.setCaretPosition(0);
×
662
                statusMessage.setText(Messages.getString("QueryDialog.Found") + " " + result.getSize() + " " + Messages.getString("QueryDialog.items") + "." +
×
663
                        " " + Messages.getString(QUERY_DIALOG_COMPILATION) + ": " + tCompiled + "ms, " + Messages.getString(QUERY_DIALOG_EXECUTION) + ": " + tResult + "ms");
×
664
            } catch (final XMLDBException e) {
×
665
                statusMessage.setText(Messages.getString(QUERY_DIALOG_ERROR) + ": " + InteractiveClient.getExceptionMessage(e) + ". " + Messages.getString(QUERY_DIALOG_COMPILATION) + ": " + tCompiled + "ms, " + Messages.getString(QUERY_DIALOG_EXECUTION) + ": " + tResult + "ms");
×
666
                progress.setVisible(false);
×
667

668

669
                ClientFrame.showErrorMessageQuery(
×
670
                        Messages.getString("QueryDialog.queryrunerrormessage") + ": "
×
671
                                + InteractiveClient.getExceptionMessage(e), e);
×
672
            } finally {
673
                if (context != null) {
×
674
                    context.runCleanupTasks();
×
675
                }
676
                context = null;
×
677
                if (result != null)
×
678
                    try {
679
                        result.clear();
×
680
                    } catch (final XMLDBException e) {
×
681
                        // ignore error
682
                    }
683
            }
684
            if (client.queryHistory.isEmpty() || !client.queryHistory.getLast().equals(xpath)) {
×
685
                client.addToHistory(xpath);
×
686
                client.writeQueryHistory();
×
687
                addQuery(xpath);
×
688
            }
689
            setCursor(Cursor.getDefaultCursor());
×
690
            progress.setVisible(false);
×
691
            killButton.setEnabled(false);
×
692
            submitButton.setEnabled(true);
×
693
        }
×
694
    }
695

696
    private void addQuery(String query) {
697
        if (query.length() > 40) {
×
698
            query = query.substring(0, 40);
×
699
        }
700
        history.addElement(Integer.toString(history.getSize() + 1) + ". " + query);
×
701
    }
×
702

703
    private class GetCollectionsListSwingWorker extends SwingWorker<Void, String> {
704
        private final String currentCollectionName;
705

706
        public GetCollectionsListSwingWorker(final String currentCollectionName) {
×
707
            this.currentCollectionName = currentCollectionName;
×
708
        }
×
709

710
        @Override
711
        public Void doInBackground() {
712
            try {
713
                final Collection root = client.getCollection(XmldbURI.ROOT_COLLECTION);
×
714
                getCollections(root, currentCollectionName);
×
715
            } catch (final XMLDBException e) {
×
716
                ClientFrame.showErrorMessage(
×
717
                        Messages.getString("QueryDialog.collectionretrievalerrormessage") + ".", e);
×
718
            }
719

720
            return null;
×
721
        }
722

723
        @Override
724
        protected void process(final List<String> collectionNames) {
725
            final int idxLast = collections.getItemCount() - 1;
×
726
            final String lastItem = collections.getItemAt(idxLast);
×
727
            if (LOADING_INDICATOR.equals(lastItem)) {
×
728
                collections.removeItemAt(idxLast);
×
729
            }
730

731
            for (final String collectionName : collectionNames) {
×
732
                collections.addItem(collectionName);
×
733
            }
734
        }
×
735

736
        private void getCollections(final Collection root, final String currentCollection) throws XMLDBException {
737
            if (isCancelled()) {
×
738
                return;
×
739
            }
740

741
            final String rootName = root.getName();
×
742
            if (!currentCollection.equals(rootName)) {
×
743
                publish(rootName);
×
744
            }
745

746
            for (String collectionName : root.listChildCollections()) {
×
747
                try (Collection child = root.getChildCollection(collectionName)) {
×
748
                    getCollections(child, currentCollection);
×
749
                } catch (final XMLDBException xmldbe) {
×
750
                    if (!(xmldbe.getCause() instanceof PermissionDeniedException)) {
×
751
                        throw xmldbe;
×
752
                    }
753
                } catch (Exception npe) {
×
754
                    System.out.println("Corrupted resource/collection skipped: " + collectionName);
×
755
                }
756
            }
757
        }
×
758
    }
759
}
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