| /* |
| * 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.dialog.menubar; |
| |
| import javax.swing.BorderFactory; |
| import javax.swing.BoxLayout; |
| import javax.swing.ButtonGroup; |
| import javax.swing.JButton; |
| import javax.swing.JCheckBox; |
| import javax.swing.JComboBox; |
| import javax.swing.JDialog; |
| import javax.swing.JFileChooser; |
| import javax.swing.JLabel; |
| import javax.swing.JOptionPane; |
| import javax.swing.JPanel; |
| import javax.swing.JRadioButton; |
| import javax.swing.JSeparator; |
| import java.awt.Dialog; |
| import java.awt.Dimension; |
| import java.awt.FlowLayout; |
| import java.awt.GridLayout; |
| import java.awt.Window; |
| import java.awt.event.ActionEvent; |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.invoke.MethodHandles; |
| import java.nio.file.Files; |
| import java.nio.file.Path; |
| import java.nio.file.Paths; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| import java.util.concurrent.Executors; |
| import java.util.stream.Collectors; |
| |
| import org.apache.logging.log4j.Logger; |
| import org.apache.lucene.luke.app.DirectoryHandler; |
| import org.apache.lucene.luke.app.IndexHandler; |
| import org.apache.lucene.luke.app.desktop.Preferences; |
| import org.apache.lucene.luke.app.desktop.PreferencesFactory; |
| 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.MessageUtils; |
| import org.apache.lucene.luke.app.desktop.util.StyleConstants; |
| import org.apache.lucene.luke.models.LukeException; |
| import org.apache.lucene.luke.util.LoggerFactory; |
| import org.apache.lucene.luke.util.reflection.ClassScanner; |
| import org.apache.lucene.store.FSDirectory; |
| import org.apache.lucene.util.NamedThreadFactory; |
| import org.apache.lucene.util.SuppressForbidden; |
| |
| /** Factory of open index dialog */ |
| public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory { |
| |
| private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| |
| private static OpenIndexDialogFactory instance; |
| |
| private final Preferences prefs; |
| |
| private final DirectoryHandler directoryHandler; |
| |
| private final IndexHandler indexHandler; |
| |
| private final JComboBox<String> idxPathCombo = new JComboBox<>(); |
| |
| private final JButton browseBtn = new JButton(); |
| |
| private final JCheckBox readOnlyCB = new JCheckBox(); |
| |
| private final JComboBox<String> dirImplCombo = new JComboBox<>(); |
| |
| private final JCheckBox noReaderCB = new JCheckBox(); |
| |
| private final JCheckBox useCompoundCB = new JCheckBox(); |
| |
| private final JRadioButton keepLastCommitRB = new JRadioButton(); |
| |
| private final JRadioButton keepAllCommitsRB = new JRadioButton(); |
| |
| private final ListenerFunctions listeners = new ListenerFunctions(); |
| |
| private JDialog dialog; |
| |
| public synchronized static OpenIndexDialogFactory getInstance() throws IOException { |
| if (instance == null) { |
| instance = new OpenIndexDialogFactory(); |
| } |
| return instance; |
| } |
| |
| private OpenIndexDialogFactory() throws IOException { |
| this.prefs = PreferencesFactory.getInstance(); |
| this.directoryHandler = DirectoryHandler.getInstance(); |
| this.indexHandler = IndexHandler.getInstance(); |
| initialize(); |
| } |
| |
| private void initialize() { |
| idxPathCombo.setPreferredSize(new Dimension(360, 40)); |
| |
| browseBtn.setText(FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse"))); |
| browseBtn.setFont(StyleConstants.FONT_BUTTON_LARGE); |
| browseBtn.setPreferredSize(new Dimension(120, 40)); |
| browseBtn.addActionListener(listeners::browseDirectory); |
| |
| readOnlyCB.setText(MessageUtils.getLocalizedMessage("openindex.checkbox.readonly")); |
| readOnlyCB.setSelected(prefs.isReadOnly()); |
| readOnlyCB.addActionListener(listeners::toggleReadOnly); |
| readOnlyCB.setOpaque(false); |
| |
| // Scanning all Directory types will take time... |
| ExecutorService executorService = Executors.newFixedThreadPool(1, new NamedThreadFactory("load-directory-types")); |
| executorService.execute(() -> { |
| for (String clazzName : supportedDirImpls()) { |
| dirImplCombo.addItem(clazzName); |
| } |
| }); |
| executorService.shutdown(); |
| dirImplCombo.setPreferredSize(new Dimension(350, 30)); |
| dirImplCombo.setSelectedItem(prefs.getDirImpl()); |
| |
| noReaderCB.setText(MessageUtils.getLocalizedMessage("openindex.checkbox.no_reader")); |
| noReaderCB.setSelected(prefs.isNoReader()); |
| noReaderCB.setOpaque(false); |
| |
| useCompoundCB.setText(MessageUtils.getLocalizedMessage("openindex.checkbox.use_compound")); |
| useCompoundCB.setSelected(prefs.isUseCompound()); |
| useCompoundCB.setOpaque(false); |
| |
| keepLastCommitRB.setText(MessageUtils.getLocalizedMessage("openindex.radio.keep_only_last_commit")); |
| keepLastCommitRB.setSelected(!prefs.isKeepAllCommits()); |
| keepLastCommitRB.setOpaque(false); |
| |
| keepAllCommitsRB.setText(MessageUtils.getLocalizedMessage("openindex.radio.keep_all_commits")); |
| keepAllCommitsRB.setSelected(prefs.isKeepAllCommits()); |
| keepAllCommitsRB.setOpaque(false); |
| |
| } |
| |
| @Override |
| public JDialog create(Window owner, String title, int width, int height) { |
| dialog = new JDialog(owner, title, Dialog.ModalityType.APPLICATION_MODAL); |
| dialog.add(content()); |
| dialog.setSize(new Dimension(width, height)); |
| dialog.setLocationRelativeTo(owner); |
| dialog.getContentPane().setBackground(prefs.getColorTheme().getBackgroundColor()); |
| return dialog; |
| } |
| |
| private JPanel content() { |
| JPanel panel = new JPanel(); |
| panel.setOpaque(false); |
| panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); |
| panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); |
| |
| panel.add(basicSettings()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(expertSettings()); |
| panel.add(new JSeparator(JSeparator.HORIZONTAL)); |
| panel.add(buttons()); |
| |
| return panel; |
| } |
| |
| private JPanel basicSettings() { |
| JPanel panel = new JPanel(new GridLayout(2, 1)); |
| panel.setOpaque(false); |
| |
| JPanel idxPath = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| idxPath.setOpaque(false); |
| idxPath.add(new JLabel(MessageUtils.getLocalizedMessage("openindex.label.index_path"))); |
| |
| idxPathCombo.removeAllItems(); |
| for (String path : prefs.getHistory()) { |
| idxPathCombo.addItem(path); |
| } |
| idxPath.add(idxPathCombo); |
| |
| idxPath.add(browseBtn); |
| |
| panel.add(idxPath); |
| |
| JPanel readOnly = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| readOnly.setOpaque(false); |
| readOnly.add(readOnlyCB); |
| JLabel roIconLB = new JLabel(FontUtils.elegantIconHtml("")); |
| readOnly.add(roIconLB); |
| panel.add(readOnly); |
| |
| return panel; |
| } |
| |
| private JPanel expertSettings() { |
| JPanel panel = new JPanel(new GridLayout(6, 1)); |
| panel.setOpaque(false); |
| |
| JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| header.setOpaque(false); |
| header.add(new JLabel(MessageUtils.getLocalizedMessage("openindex.label.expert"))); |
| panel.add(header); |
| |
| JPanel dirImpl = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| dirImpl.setOpaque(false); |
| dirImpl.add(new JLabel(MessageUtils.getLocalizedMessage("openindex.label.dir_impl"))); |
| dirImpl.add(dirImplCombo); |
| panel.add(dirImpl); |
| |
| JPanel noReader = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| noReader.setOpaque(false); |
| noReader.add(noReaderCB); |
| JLabel noReaderIcon = new JLabel(FontUtils.elegantIconHtml("")); |
| noReader.add(noReaderIcon); |
| panel.add(noReader); |
| |
| JPanel iwConfig = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| iwConfig.setOpaque(false); |
| iwConfig.add(new JLabel(MessageUtils.getLocalizedMessage("openindex.label.iw_config"))); |
| panel.add(iwConfig); |
| |
| JPanel compound = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| compound.setOpaque(false); |
| compound.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| compound.add(useCompoundCB); |
| panel.add(compound); |
| |
| JPanel keepCommits = new JPanel(new FlowLayout(FlowLayout.LEADING)); |
| keepCommits.setOpaque(false); |
| keepCommits.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 0)); |
| keepCommits.add(keepLastCommitRB); |
| keepCommits.add(keepAllCommitsRB); |
| |
| ButtonGroup group = new ButtonGroup(); |
| group.add(keepLastCommitRB); |
| group.add(keepAllCommitsRB); |
| |
| panel.add(keepCommits); |
| |
| return panel; |
| } |
| |
| private String[] supportedDirImpls() { |
| // supports FS-based built-in implementations |
| ClassScanner scanner = new ClassScanner("org.apache.lucene.store", getClass().getClassLoader()); |
| Set<Class<? extends FSDirectory>> clazzSet = scanner.scanSubTypes(FSDirectory.class); |
| |
| List<String> clazzNames = new ArrayList<>(); |
| clazzNames.add(FSDirectory.class.getName()); |
| clazzNames.addAll(clazzSet.stream().map(Class::getName).collect(Collectors.toList())); |
| |
| String[] result = new String[clazzNames.size()]; |
| return clazzNames.toArray(result); |
| } |
| |
| private JPanel buttons() { |
| JPanel panel = new JPanel(new FlowLayout(FlowLayout.TRAILING)); |
| panel.setOpaque(false); |
| panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 10, 20)); |
| |
| JButton okBtn = new JButton(MessageUtils.getLocalizedMessage("button.ok")); |
| okBtn.addActionListener(listeners::openIndexOrDirectory); |
| panel.add(okBtn); |
| |
| JButton cancelBtn = new JButton(MessageUtils.getLocalizedMessage("button.cancel")); |
| cancelBtn.addActionListener(e -> dialog.dispose()); |
| panel.add(cancelBtn); |
| |
| return panel; |
| } |
| |
| private class ListenerFunctions { |
| |
| @SuppressForbidden(reason = "FileChooser#getSelectedFile() returns java.io.File") |
| void browseDirectory(ActionEvent e) { |
| File currentDir = getLastOpenedDirectory(); |
| JFileChooser fc = currentDir == null ? new JFileChooser() : new JFileChooser(currentDir); |
| fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); |
| fc.setFileHidingEnabled(false); |
| int retVal = fc.showOpenDialog(dialog); |
| if (retVal == JFileChooser.APPROVE_OPTION) { |
| File dir = fc.getSelectedFile(); |
| idxPathCombo.insertItemAt(dir.getAbsolutePath(), 0); |
| idxPathCombo.setSelectedIndex(0); |
| } |
| } |
| |
| @SuppressForbidden(reason = "JFileChooser constructor takes java.io.File") |
| private File getLastOpenedDirectory() { |
| List<String> history = prefs.getHistory(); |
| if (!history.isEmpty()) { |
| Path path = Paths.get(history.get(0)); |
| if (Files.exists(path)) { |
| return path.getParent().toAbsolutePath().toFile(); |
| } |
| } |
| return null; |
| } |
| |
| void toggleReadOnly(ActionEvent e) { |
| setWriterConfigEnabled(!isReadOnly()); |
| } |
| |
| private void setWriterConfigEnabled(boolean enable) { |
| useCompoundCB.setEnabled(enable); |
| keepLastCommitRB.setEnabled(enable); |
| keepAllCommitsRB.setEnabled(enable); |
| } |
| |
| void openIndexOrDirectory(ActionEvent e) { |
| try { |
| if (directoryHandler.directoryOpened()) { |
| directoryHandler.close(); |
| } |
| if (indexHandler.indexOpened()) { |
| indexHandler.close(); |
| } |
| |
| String selectedPath = (String) idxPathCombo.getSelectedItem(); |
| String dirImplClazz = (String) dirImplCombo.getSelectedItem(); |
| if (selectedPath == null || selectedPath.length() == 0) { |
| String message = MessageUtils.getLocalizedMessage("openindex.message.index_path_not_selected"); |
| JOptionPane.showMessageDialog(dialog, message, "Empty index path", JOptionPane.ERROR_MESSAGE); |
| } else if (isNoReader()) { |
| directoryHandler.open(selectedPath, dirImplClazz); |
| addHistory(selectedPath); |
| } else { |
| indexHandler.open(selectedPath, dirImplClazz, isReadOnly(), useCompound(), keepAllCommits()); |
| addHistory(selectedPath); |
| } |
| prefs.setIndexOpenerPrefs( |
| isReadOnly(), dirImplClazz, |
| isNoReader(), useCompound(), keepAllCommits()); |
| closeDialog(); |
| } catch (LukeException ex) { |
| String message = ex.getMessage() + System.lineSeparator() + "See Logs tab or log file for more details."; |
| JOptionPane.showMessageDialog(dialog, message, "Invalid index path", JOptionPane.ERROR_MESSAGE); |
| } catch (Throwable cause) { |
| JOptionPane.showMessageDialog(dialog, MessageUtils.getLocalizedMessage("message.error.unknown"), "Unknown Error", JOptionPane.ERROR_MESSAGE); |
| log.error("Error opening index or directory", cause); |
| } |
| } |
| |
| private boolean isNoReader() { |
| return noReaderCB.isSelected(); |
| } |
| |
| private boolean isReadOnly() { |
| return readOnlyCB.isSelected(); |
| } |
| |
| private boolean useCompound() { |
| return useCompoundCB.isSelected(); |
| } |
| |
| private boolean keepAllCommits() { |
| return keepAllCommitsRB.isSelected(); |
| } |
| |
| private void closeDialog() { |
| dialog.dispose(); |
| } |
| |
| private void addHistory(String indexPath) throws IOException { |
| prefs.addHistory(indexPath); |
| } |
| |
| } |
| |
| } |