| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.lucene.luke.app.desktop.components; |
| |
| import javax.swing.BorderFactory; |
| import javax.swing.BoxLayout; |
| import javax.swing.JButton; |
| import javax.swing.JComboBox; |
| import javax.swing.JComponent; |
| import javax.swing.JLabel; |
| import javax.swing.JList; |
| import javax.swing.JMenuItem; |
| import javax.swing.JPanel; |
| import javax.swing.JPopupMenu; |
| import javax.swing.JScrollPane; |
| import javax.swing.JSpinner; |
| import javax.swing.JSplitPane; |
| import javax.swing.JTable; |
| import javax.swing.JTextField; |
| import javax.swing.ListSelectionModel; |
| import javax.swing.SpinnerModel; |
| import javax.swing.SpinnerNumberModel; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.table.TableCellRenderer; |
| import java.awt.BorderLayout; |
| import java.awt.Color; |
| import java.awt.Dimension; |
| import java.awt.FlowLayout; |
| import java.awt.GridBagConstraints; |
| import java.awt.GridBagLayout; |
| import java.awt.GridLayout; |
| import java.awt.Insets; |
| import java.awt.Toolkit; |
| import java.awt.datatransfer.Clipboard; |
| import java.awt.datatransfer.StringSelection; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.MouseAdapter; |
| import java.awt.event.MouseEvent; |
| import java.io.IOException; |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.List; |
| import java.util.Objects; |
| import java.util.Optional; |
| |
| import org.apache.lucene.index.DocValuesType; |
| import org.apache.lucene.index.IndexOptions; |
| import org.apache.lucene.index.Term; |
| import org.apache.lucene.luke.app.IndexHandler; |
| import org.apache.lucene.luke.app.IndexObserver; |
| import org.apache.lucene.luke.app.LukeState; |
| import org.apache.lucene.luke.app.desktop.MessageBroker; |
| import org.apache.lucene.luke.app.desktop.components.dialog.HelpDialogFactory; |
| import org.apache.lucene.luke.app.desktop.components.dialog.documents.AddDocumentDialogFactory; |
| import org.apache.lucene.luke.app.desktop.components.dialog.documents.DocValuesDialogFactory; |
| import org.apache.lucene.luke.app.desktop.components.dialog.documents.StoredValueDialogFactory; |
| import org.apache.lucene.luke.app.desktop.components.dialog.documents.TermVectorDialogFactory; |
| import org.apache.lucene.luke.app.desktop.util.DialogOpener; |
| import org.apache.lucene.luke.app.desktop.util.FontUtils; |
| import org.apache.lucene.luke.app.desktop.util.HelpHeaderRenderer; |
| import org.apache.lucene.luke.app.desktop.util.MessageUtils; |
| import org.apache.lucene.luke.app.desktop.util.StyleConstants; |
| import org.apache.lucene.luke.app.desktop.util.TableUtils; |
| import org.apache.lucene.luke.models.documents.DocValues; |
| import org.apache.lucene.luke.models.documents.DocumentField; |
| import org.apache.lucene.luke.models.documents.Documents; |
| import org.apache.lucene.luke.models.documents.DocumentsFactory; |
| import org.apache.lucene.luke.models.documents.TermPosting; |
| import org.apache.lucene.luke.models.documents.TermVectorEntry; |
| import org.apache.lucene.luke.util.BytesRefUtils; |
| |
| /** Provider of the Documents panel */ |
| public final class DocumentsPanelProvider implements DocumentsTabOperator { |
| |
| private final DocumentsFactory documentsFactory = new DocumentsFactory(); |
| |
| private final MessageBroker messageBroker; |
| |
| private final ComponentOperatorRegistry operatorRegistry; |
| |
| private final TabSwitcherProxy tabSwitcher; |
| |
| private final AddDocumentDialogFactory addDocDialogFactory; |
| |
| private final TermVectorDialogFactory tvDialogFactory; |
| |
| private final DocValuesDialogFactory dvDialogFactory; |
| |
| private final StoredValueDialogFactory valueDialogFactory; |
| |
| private final TableCellRenderer tableHeaderRenderer; |
| |
| private final JComboBox<String> fieldsCombo = new JComboBox<>(); |
| |
| private final JButton firstTermBtn = new JButton(); |
| |
| private final JTextField termTF = new JTextField(); |
| |
| private final JButton nextTermBtn = new JButton(); |
| |
| private final JTextField selectedTermTF = new JTextField(); |
| |
| private final JButton firstTermDocBtn = new JButton(); |
| |
| private final JTextField termDocIdxTF = new JTextField(); |
| |
| private final JButton nextTermDocBtn = new JButton(); |
| |
| private final JLabel termDocsNumLbl = new JLabel(); |
| |
| private final JTable posTable = new JTable(); |
| |
| private final JSpinner docNumSpnr = new JSpinner(); |
| |
| private final JLabel maxDocsLbl = new JLabel(); |
| |
| private final JButton mltBtn = new JButton(); |
| |
| private final JButton addDocBtn = new JButton(); |
| |
| private final JButton copyDocValuesBtn = new JButton(); |
| |
| private final JTable documentTable = new JTable(); |
| |
| private final JPopupMenu documentContextMenu = new JPopupMenu(); |
| |
| private final ListenerFunctions listeners = new ListenerFunctions(); |
| |
| private Documents documentsModel; |
| |
| public DocumentsPanelProvider() throws IOException { |
| this.messageBroker = MessageBroker.getInstance(); |
| this.operatorRegistry = ComponentOperatorRegistry.getInstance(); |
| this.tabSwitcher = TabSwitcherProxy.getInstance(); |
| this.addDocDialogFactory = AddDocumentDialogFactory.getInstance(); |
| this.tvDialogFactory = TermVectorDialogFactory.getInstance(); |
| this.dvDialogFactory = DocValuesDialogFactory.getInstance(); |
| this.valueDialogFactory = StoredValueDialogFactory.getInstance(); |
| HelpDialogFactory helpDialogFactory = HelpDialogFactory.getInstance(); |
| this.tableHeaderRenderer = new HelpHeaderRenderer( |
| "About Flags", "Format: IdfpoNPSB#txxVDtxxxxTx/x", |
| createFlagsHelpDialog(), helpDialogFactory); |
| |
| IndexHandler.getInstance().addObserver(new Observer()); |
| operatorRegistry.register(DocumentsTabOperator.class, this); |
| } |
| |
| private JComponent createFlagsHelpDialog() { |
| String[] values = new String[]{ |
| "I - index options(docs, frequencies, positions, offsets)", |
| "N - norms", |
| "P - payloads", |
| "S - stored", |
| "B - binary stored values", |
| "#txx - numeric stored values(type, precision)", |
| "V - term vectors", |
| "Dtxxxxx - doc values(type)", |
| "Tx/x - point values(num bytes/dimension)" |
| }; |
| JList<String> list = new JList<>(values); |
| return new JScrollPane(list); |
| } |
| |
| public JPanel get() { |
| JPanel panel = new JPanel(new GridLayout(1, 1)); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createLineBorder(Color.gray)); |
| |
| JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel()); |
| splitPane.setOpaque(false); |
| splitPane.setDividerLocation(0.4); |
| panel.add(splitPane); |
| |
| setUpDocumentContextMenu(); |
| |
| return panel; |
| } |
| |
| private JPanel initUpperPanel() { |
| JPanel panel = new JPanel(new GridBagLayout()); |
| panel.setOpaque(false); |
| GridBagConstraints c = new GridBagConstraints(); |
| |
| c.gridx = 0; |
| c.gridy = 0; |
| c.weightx = 0.5; |
| c.anchor = GridBagConstraints.FIRST_LINE_START; |
| c.fill = GridBagConstraints.HORIZONTAL; |
| panel.add(initBrowseTermsPanel(), c); |
| |
| c.gridx = 1; |
| c.gridy = 0; |
| c.weightx = 0.5; |
| c.anchor = GridBagConstraints.FIRST_LINE_START; |
| c.fill = GridBagConstraints.HORIZONTAL; |
| panel.add(initBrowseDocsByTermPanel(), c); |
| |
| return panel; |
| } |
| |
| private JPanel initBrowseTermsPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); |
| |
| JPanel top = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| top.setOpaque(false); |
| JLabel label = new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_terms")); |
| top.add(label); |
| |
| panel.add(top, BorderLayout.PAGE_START); |
| |
| JPanel center = new JPanel(new GridBagLayout()); |
| center.setOpaque(false); |
| GridBagConstraints c = new GridBagConstraints(); |
| c.fill = GridBagConstraints.BOTH; |
| |
| fieldsCombo.addActionListener(listeners::showFirstTerm); |
| c.gridx = 0; |
| c.gridy = 0; |
| c.insets = new Insets(5, 5, 5, 5); |
| c.weightx = 0.0; |
| c.gridwidth = 2; |
| center.add(fieldsCombo, c); |
| |
| firstTermBtn.setText(FontUtils.elegantIconHtml("8", MessageUtils.getLocalizedMessage("documents.button.first_term"))); |
| firstTermBtn.setMaximumSize(new Dimension(80, 30)); |
| firstTermBtn.addActionListener(listeners::showFirstTerm); |
| c.gridx = 0; |
| c.gridy = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| c.weightx = 0.2; |
| c.gridwidth = 1; |
| center.add(firstTermBtn, c); |
| |
| termTF.setColumns(20); |
| termTF.setMinimumSize(new Dimension(50, 25)); |
| termTF.setFont(StyleConstants.FONT_MONOSPACE_LARGE); |
| termTF.addActionListener(listeners::seekNextTerm); |
| c.gridx = 1; |
| c.gridy = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| c.weightx = 0.5; |
| c.gridwidth = 1; |
| center.add(termTF, c); |
| |
| nextTermBtn.setText(MessageUtils.getLocalizedMessage("documents.button.next")); |
| nextTermBtn.addActionListener(listeners::showNextTerm); |
| c.gridx = 2; |
| c.gridy = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| c.weightx = 0.1; |
| c.gridwidth = 1; |
| center.add(nextTermBtn, c); |
| |
| panel.add(center, BorderLayout.CENTER); |
| |
| JPanel footer = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 5)); |
| footer.setOpaque(false); |
| JLabel hintLbl = new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_terms_hint")); |
| footer.add(hintLbl); |
| panel.add(footer, BorderLayout.PAGE_END); |
| |
| return panel; |
| } |
| |
| private JPanel initBrowseDocsByTermPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); |
| |
| JPanel center = new JPanel(new GridBagLayout()); |
| center.setOpaque(false); |
| GridBagConstraints c = new GridBagConstraints(); |
| c.fill = GridBagConstraints.BOTH; |
| |
| JLabel label = new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_term")); |
| c.gridx = 0; |
| c.gridy = 0; |
| c.weightx = 0.0; |
| c.gridwidth = 2; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(label, c); |
| |
| selectedTermTF.setColumns(20); |
| selectedTermTF.setFont(StyleConstants.FONT_MONOSPACE_LARGE); |
| selectedTermTF.setEditable(false); |
| selectedTermTF.setBackground(Color.white); |
| c.gridx = 0; |
| c.gridy = 1; |
| c.weightx = 0.0; |
| c.gridwidth = 2; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(selectedTermTF, c); |
| |
| firstTermDocBtn.setText(FontUtils.elegantIconHtml("8", MessageUtils.getLocalizedMessage("documents.button.first_termdoc"))); |
| firstTermDocBtn.addActionListener(listeners::showFirstTermDoc); |
| c.gridx = 0; |
| c.gridy = 2; |
| c.weightx = 0.2; |
| c.gridwidth = 1; |
| c.insets = new Insets(5, 3, 5, 5); |
| center.add(firstTermDocBtn, c); |
| |
| termDocIdxTF.setEditable(false); |
| termDocIdxTF.setBackground(Color.white); |
| c.gridx = 1; |
| c.gridy = 2; |
| c.weightx = 0.5; |
| c.gridwidth = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(termDocIdxTF, c); |
| |
| nextTermDocBtn.setText(MessageUtils.getLocalizedMessage("documents.button.next")); |
| nextTermDocBtn.addActionListener(listeners::showNextTermDoc); |
| c.gridx = 2; |
| c.gridy = 2; |
| c.weightx = 0.2; |
| c.gridwidth = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(nextTermDocBtn, c); |
| |
| termDocsNumLbl.setText("in ? docs"); |
| c.gridx = 3; |
| c.gridy = 2; |
| c.weightx = 0.3; |
| c.gridwidth = 1; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(termDocsNumLbl, c); |
| |
| TableUtils.setupTable(posTable, ListSelectionModel.SINGLE_SELECTION, new PosTableModel(), null, |
| PosTableModel.Column.POSITION.getColumnWidth(), PosTableModel.Column.OFFSETS.getColumnWidth(), PosTableModel.Column.PAYLOAD.getColumnWidth()); |
| JScrollPane scrollPane = new JScrollPane(posTable); |
| scrollPane.setMinimumSize(new Dimension(100, 100)); |
| c.gridx = 0; |
| c.gridy = 3; |
| c.gridwidth = 4; |
| c.insets = new Insets(5, 5, 5, 5); |
| center.add(scrollPane, c); |
| |
| panel.add(center, BorderLayout.CENTER); |
| |
| return panel; |
| } |
| |
| private JPanel initLowerPanel() { |
| JPanel panel = new JPanel(new BorderLayout()); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); |
| |
| JPanel browseDocsPanel = new JPanel(); |
| browseDocsPanel.setOpaque(false); |
| browseDocsPanel.setLayout(new BoxLayout(browseDocsPanel, BoxLayout.PAGE_AXIS)); |
| browseDocsPanel.add(initBrowseDocsBar()); |
| |
| JPanel browseDocsNote1 = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| browseDocsNote1.setOpaque(false); |
| browseDocsNote1.add(new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note1"))); |
| browseDocsPanel.add(browseDocsNote1); |
| |
| JPanel browseDocsNote2 = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| browseDocsNote2.setOpaque(false); |
| browseDocsNote2.add(new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note2"))); |
| browseDocsPanel.add(browseDocsNote2); |
| |
| panel.add(browseDocsPanel, BorderLayout.PAGE_START); |
| |
| TableUtils.setupTable(documentTable, ListSelectionModel.MULTIPLE_INTERVAL_SELECTION, new DocumentsTableModel(), new MouseAdapter() { |
| @Override |
| public void mouseClicked(MouseEvent e) { |
| listeners.showDocumentContextMenu(e); |
| } |
| }, |
| DocumentsTableModel.Column.FIELD.getColumnWidth(), |
| DocumentsTableModel.Column.FLAGS.getColumnWidth(), |
| DocumentsTableModel.Column.NORM.getColumnWidth(), |
| DocumentsTableModel.Column.VALUE.getColumnWidth()); |
| JPanel flagsHeader = new JPanel(new FlowLayout(FlowLayout.CENTER)); |
| flagsHeader.setOpaque(false); |
| flagsHeader.add(new JLabel("Flags")); |
| flagsHeader.add(new JLabel("Help")); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setHeaderValue(flagsHeader); |
| |
| JScrollPane scrollPane = new JScrollPane(documentTable); |
| scrollPane.getHorizontalScrollBar().setAutoscrolls(false); |
| panel.add(scrollPane, BorderLayout.CENTER); |
| |
| return panel; |
| } |
| |
| private JPanel initBrowseDocsBar() { |
| JPanel panel = new JPanel(new GridLayout(1, 2)); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 5)); |
| |
| JPanel left = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 2)); |
| left.setOpaque(false); |
| JLabel label = new JLabel(FontUtils.elegantIconHtml("h", MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_idx"))); |
| label.setHorizontalTextPosition(JLabel.LEFT); |
| left.add(label); |
| docNumSpnr.setPreferredSize(new Dimension(100, 25)); |
| docNumSpnr.addChangeListener(listeners::showCurrentDoc); |
| left.add(docNumSpnr); |
| maxDocsLbl.setText("in ? docs"); |
| left.add(maxDocsLbl); |
| panel.add(left); |
| |
| JPanel right = new JPanel(new FlowLayout(FlowLayout.TRAILING)); |
| right.setOpaque(false); |
| copyDocValuesBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("documents.buttont.copy_values"))); |
| copyDocValuesBtn.setMargin(new Insets(5, 0, 5, 0)); |
| copyDocValuesBtn.addActionListener(listeners::copySelectedOrAllStoredValues); |
| right.add(copyDocValuesBtn); |
| mltBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("documents.button.mlt"))); |
| mltBtn.setMargin(new Insets(5, 0, 5, 0)); |
| mltBtn.addActionListener(listeners::mltSearch); |
| right.add(mltBtn); |
| addDocBtn.setText(FontUtils.elegantIconHtml("Y", MessageUtils.getLocalizedMessage("documents.button.add"))); |
| addDocBtn.setMargin(new Insets(5, 0, 5, 0)); |
| addDocBtn.addActionListener(listeners::showAddDocumentDialog); |
| right.add(addDocBtn); |
| panel.add(right); |
| |
| return panel; |
| } |
| |
| private void setUpDocumentContextMenu() { |
| // show term vector |
| JMenuItem item1 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item1")); |
| item1.addActionListener(listeners::showTermVectorDialog); |
| documentContextMenu.add(item1); |
| |
| // show doc values |
| JMenuItem item2 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item2")); |
| item2.addActionListener(listeners::showDocValuesDialog); |
| documentContextMenu.add(item2); |
| |
| // show stored value |
| JMenuItem item3 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item3")); |
| item3.addActionListener(listeners::showStoredValueDialog); |
| documentContextMenu.add(item3); |
| |
| // copy stored value to clipboard |
| JMenuItem item4 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item4")); |
| item4.addActionListener(listeners::copyStoredValue); |
| documentContextMenu.add(item4); |
| } |
| |
| // control methods |
| |
| private void showFirstTerm() { |
| String fieldName = (String) fieldsCombo.getSelectedItem(); |
| if (fieldName == null || fieldName.length() == 0) { |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.field.message.not_selected")); |
| return; |
| } |
| |
| termDocIdxTF.setText(""); |
| clearPosTable(); |
| |
| Optional<Term> firstTerm = documentsModel.firstTerm(fieldName); |
| String firstTermText = firstTerm.map(Term::text).orElse(""); |
| termTF.setText(firstTermText); |
| selectedTermTF.setText(firstTermText); |
| if (firstTerm.isPresent()) { |
| String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?"); |
| termDocsNumLbl.setText("in " + num + " docs"); |
| |
| nextTermBtn.setEnabled(true); |
| termTF.setEditable(true); |
| firstTermDocBtn.setEnabled(true); |
| } else { |
| nextTermBtn.setEnabled(false); |
| termTF.setEditable(false); |
| firstTermDocBtn.setEnabled(false); |
| } |
| nextTermDocBtn.setEnabled(false); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void showNextTerm() { |
| termDocIdxTF.setText(""); |
| clearPosTable(); |
| |
| Optional<Term> nextTerm = documentsModel.nextTerm(); |
| String nextTermText = nextTerm.map(Term::text).orElse(""); |
| termTF.setText(nextTermText); |
| selectedTermTF.setText(nextTermText); |
| if (nextTerm.isPresent()) { |
| String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?"); |
| termDocsNumLbl.setText("in " + num + " docs"); |
| |
| termTF.setEditable(true); |
| firstTermDocBtn.setEnabled(true); |
| } else { |
| nextTermBtn.setEnabled(false); |
| termTF.setEditable(false); |
| firstTermDocBtn.setEnabled(false); |
| } |
| nextTermDocBtn.setEnabled(false); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| @Override |
| public void seekNextTerm() { |
| termDocIdxTF.setText(""); |
| posTable.setModel(new PosTableModel()); |
| |
| String termText = termTF.getText(); |
| |
| Optional<Term> nextTerm = documentsModel.seekTerm(termText); |
| String nextTermText = nextTerm.map(Term::text).orElse(""); |
| termTF.setText(nextTermText); |
| selectedTermTF.setText(nextTermText); |
| if (nextTerm.isPresent()) { |
| String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?"); |
| termDocsNumLbl.setText("in " + num + " docs"); |
| |
| termTF.setEditable(true); |
| firstTermDocBtn.setEnabled(true); |
| } else { |
| nextTermBtn.setEnabled(false); |
| termTF.setEditable(false); |
| firstTermDocBtn.setEnabled(false); |
| } |
| nextTermDocBtn.setEnabled(false); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| |
| private void clearPosTable() { |
| TableUtils.setupTable(posTable, ListSelectionModel.SINGLE_SELECTION, new PosTableModel(), null, |
| PosTableModel.Column.POSITION.getColumnWidth(), |
| PosTableModel.Column.OFFSETS.getColumnWidth(), |
| PosTableModel.Column.PAYLOAD.getColumnWidth()); |
| } |
| |
| @Override |
| public void showFirstTermDoc() { |
| int docid = documentsModel.firstTermDoc().orElse(-1); |
| if (docid < 0) { |
| nextTermDocBtn.setEnabled(false); |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available")); |
| return; |
| } |
| termDocIdxTF.setText(String.valueOf(1)); |
| displayDoc(docid); |
| |
| List<TermPosting> postings = documentsModel.getTermPositions(); |
| posTable.setModel(new PosTableModel(postings)); |
| posTable.getColumnModel().getColumn(PosTableModel.Column.POSITION.getIndex()).setPreferredWidth(PosTableModel.Column.POSITION.getColumnWidth()); |
| posTable.getColumnModel().getColumn(PosTableModel.Column.OFFSETS.getIndex()).setPreferredWidth(PosTableModel.Column.OFFSETS.getColumnWidth()); |
| posTable.getColumnModel().getColumn(PosTableModel.Column.PAYLOAD.getIndex()).setPreferredWidth(PosTableModel.Column.PAYLOAD.getColumnWidth()); |
| |
| nextTermDocBtn.setEnabled(true); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void showNextTermDoc() { |
| int docid = documentsModel.nextTermDoc().orElse(-1); |
| if (docid < 0) { |
| nextTermDocBtn.setEnabled(false); |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available")); |
| return; |
| } |
| int curIdx = Integer.parseInt(termDocIdxTF.getText()); |
| termDocIdxTF.setText(String.valueOf(curIdx + 1)); |
| displayDoc(docid); |
| |
| List<TermPosting> postings = documentsModel.getTermPositions(); |
| posTable.setModel(new PosTableModel(postings)); |
| |
| nextTermDocBtn.setDefaultCapable(true); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void showCurrentDoc() { |
| int docid = (Integer) docNumSpnr.getValue(); |
| displayDoc(docid); |
| } |
| |
| private void mltSearch() { |
| int docNum = (int) docNumSpnr.getValue(); |
| operatorRegistry.get(SearchTabOperator.class).ifPresent(operator -> { |
| operator.mltSearch(docNum); |
| tabSwitcher.switchTab(TabbedPaneProvider.Tab.SEARCH); |
| }); |
| } |
| |
| private void showAddDocumentDialog() { |
| new DialogOpener<>(addDocDialogFactory).open("Add document", 600, 500, |
| (factory) -> { |
| }); |
| } |
| |
| private void showTermVectorDialog() { |
| int docid = (Integer) docNumSpnr.getValue(); |
| String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex()); |
| List<TermVectorEntry> tvEntries = documentsModel.getTermVectors(docid, field); |
| if (tvEntries.isEmpty()) { |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termvector.message.not_available", field, docid)); |
| return; |
| } |
| |
| new DialogOpener<>(tvDialogFactory).open( |
| "Term Vector", 600, 400, |
| (factory) -> { |
| factory.setField(field); |
| factory.setTvEntries(tvEntries); |
| }); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void showDocValuesDialog() { |
| int docid = (Integer) docNumSpnr.getValue(); |
| String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex()); |
| Optional<DocValues> docValues = documentsModel.getDocValues(docid, field); |
| if (docValues.isPresent()) { |
| new DialogOpener<>(dvDialogFactory).open( |
| "Doc Values", 400, 300, |
| (factory) -> { |
| factory.setValue(field, docValues.get()); |
| }); |
| messageBroker.clearStatusMessage(); |
| } else { |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.docvalues.message.not_available", field, docid)); |
| } |
| } |
| |
| private void showStoredValueDialog() { |
| int docid = (Integer) docNumSpnr.getValue(); |
| String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex()); |
| String value = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex()); |
| if (Objects.isNull(value)) { |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid)); |
| return; |
| } |
| new DialogOpener<>(valueDialogFactory).open( |
| "Stored Value", 400, 300, |
| (factory) -> { |
| factory.setField(field); |
| factory.setValue(value); |
| }); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void copyStoredValue() { |
| int docid = (Integer) docNumSpnr.getValue(); |
| String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex()); |
| String value = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex()); |
| if (Objects.isNull(value)) { |
| messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid)); |
| return; |
| } |
| Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| StringSelection selection = new StringSelection(value); |
| clipboard.setContents(selection, null); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private void copySelectedOrAllStoredValues() { |
| StringSelection selection; |
| if (documentTable.getSelectedRowCount() == 0) { |
| selection = copyAllValues(); |
| } else { |
| selection = copySelectedValues(); |
| } |
| Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); |
| clipboard.setContents(selection, null); |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private StringSelection copyAllValues() { |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < documentTable.getRowCount(); i++) { |
| String value = (String) documentTable.getModel().getValueAt(i, DocumentsTableModel.Column.VALUE.getIndex()); |
| if (Objects.nonNull(value)) { |
| sb.append((i == 0) ? value : System.lineSeparator() + value); |
| } |
| } |
| return new StringSelection(sb.toString()); |
| } |
| |
| private StringSelection copySelectedValues() { |
| StringBuilder sb = new StringBuilder(); |
| boolean isFirst = true; |
| for (int rowIndex : documentTable.getSelectedRows()) { |
| String value = (String) documentTable.getModel().getValueAt(rowIndex, DocumentsTableModel.Column.VALUE.getIndex()); |
| if (Objects.nonNull(value)) { |
| sb.append(isFirst ? value : System.lineSeparator() + value); |
| isFirst = false; |
| } |
| } |
| return new StringSelection(sb.toString()); |
| } |
| |
| @Override |
| public void browseTerm(String field, String term) { |
| fieldsCombo.setSelectedItem(field); |
| termTF.setText(term); |
| seekNextTerm(); |
| showFirstTermDoc(); |
| } |
| |
| @Override |
| public void displayLatestDoc() { |
| int docid = documentsModel.getMaxDoc() - 1; |
| showDoc(docid); |
| } |
| |
| @Override |
| public void displayDoc(int docid) { |
| showDoc(docid); |
| } |
| |
| ; |
| |
| private void showDoc(int docid) { |
| docNumSpnr.setValue(docid); |
| |
| List<DocumentField> doc = documentsModel.getDocumentFields(docid); |
| documentTable.setModel(new DocumentsTableModel(doc)); |
| documentTable.setFont(StyleConstants.FONT_MONOSPACE_LARGE); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FIELD.getIndex()).setPreferredWidth(DocumentsTableModel.Column.FIELD.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setMinWidth(DocumentsTableModel.Column.FLAGS.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setMaxWidth(DocumentsTableModel.Column.FIELD.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.NORM.getIndex()).setMinWidth(DocumentsTableModel.Column.NORM.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.NORM.getIndex()).setMaxWidth(DocumentsTableModel.Column.NORM.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.VALUE.getIndex()).setPreferredWidth(DocumentsTableModel.Column.VALUE.getColumnWidth()); |
| documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setHeaderRenderer(tableHeaderRenderer); |
| |
| messageBroker.clearStatusMessage(); |
| } |
| |
| private class ListenerFunctions { |
| |
| void showFirstTerm(ActionEvent e) { |
| DocumentsPanelProvider.this.showFirstTerm(); |
| } |
| |
| void seekNextTerm(ActionEvent e) { |
| DocumentsPanelProvider.this.seekNextTerm(); |
| } |
| |
| void showNextTerm(ActionEvent e) { |
| DocumentsPanelProvider.this.showNextTerm(); |
| } |
| |
| void showFirstTermDoc(ActionEvent e) { |
| DocumentsPanelProvider.this.showFirstTermDoc(); |
| } |
| |
| void showNextTermDoc(ActionEvent e) { |
| DocumentsPanelProvider.this.showNextTermDoc(); |
| } |
| |
| void showCurrentDoc(ChangeEvent e) { |
| DocumentsPanelProvider.this.showCurrentDoc(); |
| } |
| |
| void mltSearch(ActionEvent e) { |
| DocumentsPanelProvider.this.mltSearch(); |
| } |
| |
| void showAddDocumentDialog(ActionEvent e) { |
| DocumentsPanelProvider.this.showAddDocumentDialog(); |
| } |
| |
| void showDocumentContextMenu(MouseEvent e) { |
| if (e.getClickCount() == 2 && !e.isConsumed()) { |
| int row = documentTable.rowAtPoint(e.getPoint()); |
| if (row != documentTable.getSelectedRow()) { |
| documentTable.changeSelection(row, documentTable.getSelectedColumn(), false, false); |
| } |
| documentContextMenu.show(e.getComponent(), e.getX(), e.getY()); |
| } |
| } |
| |
| void showTermVectorDialog(ActionEvent e) { |
| DocumentsPanelProvider.this.showTermVectorDialog(); |
| } |
| |
| void showDocValuesDialog(ActionEvent e) { |
| DocumentsPanelProvider.this.showDocValuesDialog(); |
| } |
| |
| void showStoredValueDialog(ActionEvent e) { |
| DocumentsPanelProvider.this.showStoredValueDialog(); |
| } |
| |
| void copyStoredValue(ActionEvent e) { |
| DocumentsPanelProvider.this.copyStoredValue(); |
| } |
| |
| void copySelectedOrAllStoredValues(ActionEvent e) { |
| DocumentsPanelProvider.this.copySelectedOrAllStoredValues(); |
| } |
| |
| } |
| |
| private class Observer implements IndexObserver { |
| |
| @Override |
| public void openIndex(LukeState state) { |
| documentsModel = documentsFactory.newInstance(state.getIndexReader()); |
| |
| addDocBtn.setEnabled(!state.readOnly() && state.hasDirectoryReader()); |
| |
| int maxDoc = documentsModel.getMaxDoc(); |
| maxDocsLbl.setText("in " + maxDoc + " docs"); |
| if (maxDoc > 0) { |
| int max = Math.max(maxDoc - 1, 0); |
| SpinnerModel spinnerModel = new SpinnerNumberModel(0, 0, max, 1); |
| docNumSpnr.setModel(spinnerModel); |
| docNumSpnr.setEnabled(true); |
| displayDoc(0); |
| } else { |
| docNumSpnr.setEnabled(false); |
| } |
| |
| documentsModel.getFieldNames().stream().sorted().forEach(fieldsCombo::addItem); |
| } |
| |
| @Override |
| public void closeIndex() { |
| maxDocsLbl.setText("in ? docs"); |
| docNumSpnr.setEnabled(false); |
| fieldsCombo.removeAllItems(); |
| termTF.setText(""); |
| selectedTermTF.setText(""); |
| termDocsNumLbl.setText(""); |
| termDocIdxTF.setText(""); |
| |
| posTable.setModel(new PosTableModel()); |
| documentTable.setModel(new DocumentsTableModel()); |
| } |
| } |
| |
| static final class PosTableModel extends TableModelBase<PosTableModel.Column> { |
| |
| enum Column implements TableColumnInfo { |
| |
| POSITION("Position", 0, Integer.class, 80), |
| OFFSETS("Offsets", 1, String.class, 120), |
| PAYLOAD("Payload", 2, String.class, 300); |
| |
| private final String colName; |
| private final int index; |
| private final Class<?> type; |
| private final int width; |
| |
| Column(String colName, int index, Class<?> type, int width) { |
| this.colName = colName; |
| this.index = index; |
| this.type = type; |
| this.width = width; |
| } |
| |
| @Override |
| public String getColName() { |
| return colName; |
| } |
| |
| @Override |
| public int getIndex() { |
| return index; |
| } |
| |
| @Override |
| public Class<?> getType() { |
| return type; |
| } |
| |
| @Override |
| public int getColumnWidth() { |
| return width; |
| } |
| } |
| |
| PosTableModel() { |
| super(); |
| } |
| |
| PosTableModel(List<TermPosting> postings) { |
| super(postings.size()); |
| |
| for (int i = 0; i < postings.size(); i++) { |
| TermPosting p = postings.get(i); |
| |
| int position = postings.get(i).getPosition(); |
| String offset = null; |
| if (p.getStartOffset() >= 0 && p.getEndOffset() >= 0) { |
| offset = p.getStartOffset() + "-" + p.getEndOffset(); |
| } |
| String payload = null; |
| if (p.getPayload() != null) { |
| payload = BytesRefUtils.decode(p.getPayload()); |
| } |
| |
| data[i] = new Object[]{position, offset, payload}; |
| } |
| } |
| |
| @Override |
| protected Column[] columnInfos() { |
| return Column.values(); |
| } |
| } |
| |
| static final class DocumentsTableModel extends TableModelBase<DocumentsTableModel.Column> { |
| |
| enum Column implements TableColumnInfo { |
| FIELD("Field", 0, String.class, 150), |
| FLAGS("Flags", 1, String.class, 200), |
| NORM("Norm", 2, Long.class, 80), |
| VALUE("Value", 3, String.class, 500); |
| |
| private final String colName; |
| private final int index; |
| private final Class<?> type; |
| private final int width; |
| |
| Column(String colName, int index, Class<?> type, int width) { |
| this.colName = colName; |
| this.index = index; |
| this.type = type; |
| this.width = width; |
| } |
| |
| @Override |
| public String getColName() { |
| return colName; |
| } |
| |
| @Override |
| public int getIndex() { |
| return index; |
| } |
| |
| @Override |
| public Class<?> getType() { |
| return type; |
| } |
| |
| @Override |
| public int getColumnWidth() { |
| return width; |
| } |
| } |
| |
| DocumentsTableModel() { |
| super(); |
| } |
| |
| DocumentsTableModel(List<DocumentField> doc) { |
| super(doc.size()); |
| |
| for (int i = 0; i < doc.size(); i++) { |
| DocumentField docField = doc.get(i); |
| String field = docField.getName(); |
| String flags = flags(docField); |
| long norm = docField.getNorm(); |
| String value = null; |
| if (docField.getStringValue() != null) { |
| value = docField.getStringValue(); |
| } else if (docField.getNumericValue() != null) { |
| value = String.valueOf(docField.getNumericValue()); |
| } else if (docField.getBinaryValue() != null) { |
| value = String.valueOf(docField.getBinaryValue()); |
| } |
| data[i] = new Object[]{field, flags, norm, value}; |
| } |
| } |
| |
| private static String flags(org.apache.lucene.luke.models.documents.DocumentField f) { |
| StringBuilder sb = new StringBuilder(); |
| // index options |
| if (f.getIdxOptions() == null || f.getIdxOptions() == IndexOptions.NONE) { |
| sb.append("-----"); |
| } else { |
| sb.append("I"); |
| switch (f.getIdxOptions()) { |
| case DOCS: |
| sb.append("d---"); |
| break; |
| case DOCS_AND_FREQS: |
| sb.append("df--"); |
| break; |
| case DOCS_AND_FREQS_AND_POSITIONS: |
| sb.append("dfp-"); |
| break; |
| case DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS: |
| sb.append("dfpo"); |
| break; |
| default: |
| sb.append("----"); |
| } |
| } |
| // has norm? |
| if (f.hasNorms()) { |
| sb.append("N"); |
| } else { |
| sb.append("-"); |
| } |
| // has payloads? |
| if (f.hasPayloads()) { |
| sb.append("P"); |
| } else { |
| sb.append("-"); |
| } |
| // stored? |
| if (f.isStored()) { |
| sb.append("S"); |
| } else { |
| sb.append("-"); |
| } |
| // binary? |
| if (f.getBinaryValue() != null) { |
| sb.append("B"); |
| } else { |
| sb.append("-"); |
| } |
| // numeric? |
| if (f.getNumericValue() == null) { |
| sb.append("----"); |
| } else { |
| sb.append("#"); |
| // try faking it |
| Number numeric = f.getNumericValue(); |
| if (numeric instanceof Integer) { |
| sb.append("i32"); |
| } else if (numeric instanceof Long) { |
| sb.append("i64"); |
| } else if (numeric instanceof Float) { |
| sb.append("f32"); |
| } else if (numeric instanceof Double) { |
| sb.append("f64"); |
| } else if (numeric instanceof Short) { |
| sb.append("i16"); |
| } else if (numeric instanceof Byte) { |
| sb.append("i08"); |
| } else if (numeric instanceof BigDecimal) { |
| sb.append("b^d"); |
| } else if (numeric instanceof BigInteger) { |
| sb.append("b^i"); |
| } else { |
| sb.append("???"); |
| } |
| } |
| // has term vector? |
| if (f.hasTermVectors()) { |
| sb.append("V"); |
| } else { |
| sb.append("-"); |
| } |
| // doc values |
| if (f.getDvType() == null || f.getDvType() == DocValuesType.NONE) { |
| sb.append("-------"); |
| } else { |
| sb.append("D"); |
| switch (f.getDvType()) { |
| case NUMERIC: |
| sb.append("number"); |
| break; |
| case BINARY: |
| sb.append("binary"); |
| break; |
| case SORTED: |
| sb.append("sorted"); |
| break; |
| case SORTED_NUMERIC: |
| sb.append("srtnum"); |
| break; |
| case SORTED_SET: |
| sb.append("srtset"); |
| break; |
| default: |
| sb.append("??????"); |
| } |
| } |
| // point values |
| if (f.getPointDimensionCount() == 0) { |
| sb.append("----"); |
| } else { |
| sb.append("T"); |
| sb.append(f.getPointNumBytes()); |
| sb.append("/"); |
| sb.append(f.getPointDimensionCount()); |
| } |
| return sb.toString(); |
| } |
| |
| @Override |
| protected Column[] columnInfos() { |
| return Column.values(); |
| } |
| } |
| |
| } |
| |