You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2018/07/17 21:12:44 UTC
[09/48] lucene-solr:jira/lucene-2562: Add luke as a sub-module.
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/DocumentsController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/DocumentsController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/DocumentsController.java
new file mode 100644
index 0000000..4247459
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/DocumentsController.java
@@ -0,0 +1,581 @@
+/*
+ * 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.controllers;
+
+import com.google.inject.Inject;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.geometry.Orientation;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.ChoiceBox;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
+import javafx.scene.input.KeyCode;
+import javafx.scene.layout.FlowPane;
+import javafx.stage.Stage;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.luke.app.IndexObserver;
+import org.apache.lucene.luke.app.LukeState;
+import org.apache.lucene.luke.app.controllers.dialog.documents.AddDocumentController;
+import org.apache.lucene.luke.app.controllers.dialog.documents.DocValuesController;
+import org.apache.lucene.luke.app.controllers.dialog.HelpController;
+import org.apache.lucene.luke.app.controllers.dialog.documents.StoredValueController;
+import org.apache.lucene.luke.app.controllers.dialog.documents.TermVectorController;
+import org.apache.lucene.luke.app.controllers.dto.documents.DocumentField;
+import org.apache.lucene.luke.app.controllers.dto.documents.TermPosting;
+import org.apache.lucene.luke.app.util.DialogOpener;
+import org.apache.lucene.luke.app.util.IntegerTextFormatter;
+import org.apache.lucene.luke.models.LukeException;
+import org.apache.lucene.luke.models.documents.DocValues;
+import org.apache.lucene.luke.models.documents.Documents;
+import org.apache.lucene.luke.models.documents.DocumentsFactory;
+import org.apache.lucene.luke.models.documents.TermVectorEntry;
+import org.apache.lucene.luke.app.util.MessageUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class DocumentsController extends ChildTabController implements IndexObserver {
+
+ private final DocumentsFactory documentsFactory;
+
+ private Documents documentsModel;
+
+ private Analyzer currentAnalyzer;
+
+ @FXML
+ private Spinner<Integer> docNum;
+
+ @FXML
+ private Hyperlink mltSearch;
+
+ @FXML
+ private Button addDoc;
+
+ @FXML
+ private Label maxDocs;
+
+ @FXML
+ private ChoiceBox<String> field;
+
+ @FXML
+ private TextField term;
+
+ @FXML
+ private Button firstTerm;
+
+ @FXML
+ private Button nextTerm;
+
+ @FXML
+ private Label showedDocNum;
+
+ @FXML
+ private TextField showedTerm;
+
+ @FXML
+ private Label termDocsNum;
+
+ @FXML
+ private TextField termDocIdx;
+
+ @FXML
+ private Button firstTermDoc;
+
+ @FXML
+ private Button nextTermDoc;
+
+ @FXML
+ private TableView<TermPosting> posTable;
+
+ @FXML
+ private TableColumn<TermPosting, Integer> posColumn;
+
+ @FXML
+ private TableColumn<TermPosting, String> offsetColumn;
+
+ @FXML
+ private TableColumn<TermPosting, String> payloadColumn;
+
+ private ObservableList<TermPosting> posList;
+
+ @FXML
+ private TableView<DocumentField> documentTable;
+
+ @FXML
+ private TableColumn<DocumentField, String> fieldColumn;
+
+ @FXML
+ private TableColumn<DocumentField, String> flagColumn;
+
+ @FXML
+ private TableColumn<DocumentField, Long> normColumn;
+
+ @FXML
+ private TableColumn<DocumentField, String> valueColumn;
+
+ private ObservableList<DocumentField> documentFieldList;
+
+ @Inject
+ public DocumentsController(DocumentsFactory documentsFactory) {
+ this.documentsFactory = documentsFactory;
+ }
+
+ @FXML
+ private void initialize() {
+ // initialize postings table view
+ posColumn.setCellValueFactory(new PropertyValueFactory<>("position"));
+ offsetColumn.setCellValueFactory(new PropertyValueFactory<>("offset"));
+ payloadColumn.setCellValueFactory(new PropertyValueFactory<>("payload"));
+ posList = FXCollections.observableArrayList();
+ posTable.setItems(posList);
+
+ // initialize documents table view
+ fieldColumn.setCellValueFactory(new PropertyValueFactory<>("field"));
+ flagColumn.setCellValueFactory(new PropertyValueFactory<>("flag"));
+ normColumn.setCellValueFactory(new PropertyValueFactory<>("norm"));
+ valueColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
+ documentFieldList = FXCollections.observableArrayList();
+ documentTable.setItems(documentFieldList);
+ documentTable.setContextMenu(createDocTableMenu());
+
+ ImageView imageView = new ImageView(new Image("/img/icon_question_alt2.png"));
+ imageView.setFitWidth(12);
+ imageView.setFitHeight(12);
+ Hyperlink helpLink = new Hyperlink(MessageUtils.getLocalizedMessage("label.help"), imageView);
+ helpLink.setOnMouseClicked(e -> runnableWrapper(this::showFlagsHelpDialog));
+ Label flagLabel = new Label("Flags");
+ flagLabel.setPadding(new Insets(0, 30, 0, 0));
+ FlowPane flowPane = new FlowPane();
+ flowPane.setOrientation(Orientation.HORIZONTAL);
+ flowPane.setMaxWidth(Double.MAX_VALUE);
+ flowPane.setAlignment(Pos.CENTER);
+ flowPane.getChildren().addAll(flagLabel, helpLink);
+ flagColumn.setGraphic(flowPane);
+
+ firstTerm.setOnAction(e -> runnableWrapper(this::showFirstTerm));
+ nextTerm.setOnAction(e -> runnableWrapper(this::showNextTerm));
+ term.setOnKeyPressed(e -> runnableWrapper(() -> {
+ if (e.getCode() == KeyCode.ENTER) {
+ seekTermCeil();
+ }
+ }));
+
+ firstTermDoc.setOnAction(e -> runnableWrapper(this::showFirstTermDoc));
+ nextTermDoc.setOnAction(e -> runnableWrapper(this::showNextTermDoc));
+
+ nextTerm.setDisable(true);
+ term.setEditable(false);
+ firstTermDoc.setDisable(true);
+ nextTermDoc.setDisable(true);
+
+ mltSearch.setOnAction(e -> runnableWrapper(() -> {
+ getSearchController().mltSearch(docNum.getValue());
+ switchTab(LukeController.Tab.SEARCH);
+ }));
+
+ addDoc.setOnAction(e -> runnableWrapper(this::showAddDocumentDialog));
+ }
+
+ @Override
+ public void openIndex(LukeState state) throws LukeException {
+ documentsModel = documentsFactory.newInstance(state.getIndexReader());
+
+ addDoc.setDisable(state.readOnly() || !state.hasDirectoryReader());
+
+ int maxDoc = documentsModel.getMaxDoc();
+ maxDocs.setText(String.format("in %d docs", maxDoc));
+ if (maxDoc > 0) {
+ int max = Math.max(maxDoc - 1, 0);
+ SpinnerValueFactory.IntegerSpinnerValueFactory valueFactory =
+ new SpinnerValueFactory.IntegerSpinnerValueFactory(0, max, 0, 1);
+ docNum.setValueFactory(valueFactory);
+ TextFormatter<Integer> textFormatter = new IntegerTextFormatter(valueFactory.getConverter(), 0);
+ docNum.getEditor().setTextFormatter(textFormatter);
+ docNum.valueProperty().addListener(e -> runnableWrapper(() ->
+ showDoc(docNum.getValueFactory().getValue())
+ ));
+ docNum.focusedProperty().addListener((obs, oldV, newV) -> runnableWrapper(() -> {
+ if (newV) {
+ // won't not change value, but commit editor
+ // https://stackoverflow.com/questions/32340476/manually-typing-in-text-in-javafx-spinner-is-not-updating-the-value-unless-user
+ docNum.increment(0);
+ showDoc(docNum.getValueFactory().getValue());
+ }
+ }));
+ } else {
+ docNum.setDisable(true);
+ }
+
+ List<String> fields = documentsModel.getFieldNames().stream().sorted().collect(Collectors.toList());
+ field.getItems().addAll(fields);
+ if (fields.size() > 0) {
+ field.setValue(fields.get(0));
+ }
+ field.setOnAction(e -> runnableWrapper(this::showFirstTerm));
+
+ if (documentsModel.getMaxDoc() > 0) {
+ showDoc(0);
+ }
+ }
+
+ @Override
+ public void closeIndex() {
+ documentsModel = null;
+
+ maxDocs.setText("in ? docs");
+ docNum.getEditor().setText("");
+ field.getItems().clear();
+ term.setText("");
+ showedDocNum.setText("");
+ showedTerm.setText("");
+ termDocsNum.setText("");
+ termDocIdx.setText("");
+ posList.clear();
+ documentFieldList.clear();
+ }
+
+ private void showDoc(int docid) throws LukeException {
+ showedDocNum.setText(String.valueOf(docid));
+ documentFieldList.clear();
+
+ List<DocumentField> doc = documentsModel.getDocumentFields(docid).stream()
+ .map(DocumentField::of)
+ .collect(Collectors.toList());
+ documentFieldList.addAll(doc);
+ clearStatusMessage();
+ }
+
+ private void showFirstTerm() {
+ String fieldName = field.getValue();
+ if (fieldName == null || fieldName.length() == 0) {
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.field.message.not_selected"));
+ return;
+ }
+
+ termDocIdx.setText("");
+ posList.clear();
+
+ String firstTermText = documentsModel.firstTerm(fieldName).map(Term::text).orElse("");
+ term.setText(firstTermText);
+ showedTerm.setText(firstTermText);
+ if (showedTerm.getText().length() > 0) {
+ String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?");
+ termDocsNum.setText(String.format("in %s docs", num));
+
+ nextTerm.setDisable(false);
+ term.setEditable(true);
+ firstTermDoc.setDisable(false);
+ } else {
+ nextTerm.setDisable(true);
+ term.setEditable(false);
+ firstTermDoc.setDisable(true);
+ }
+ nextTermDoc.setDisable(true);
+
+ }
+
+ private void showNextTerm() throws LukeException {
+ termDocIdx.setText("");
+ posList.clear();
+
+ String nextTermText = documentsModel.nextTerm().map(Term::text).orElse("");
+ term.setText(nextTermText);
+ showedTerm.setText(nextTermText);
+ if (showedTerm.getText().length() > 0) {
+ String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?");
+ termDocsNum.setText(String.format("in %s docs", num));
+
+ term.setEditable(true);
+ firstTermDoc.setDisable(false);
+ } else {
+ nextTerm.setDisable(true);
+ term.setEditable(false);
+ firstTermDoc.setDisable(true);
+ }
+ nextTermDoc.setDisable(true);
+
+ clearStatusMessage();
+ }
+
+ private void seekTermCeil() throws LukeException {
+ termDocIdx.setText("");
+ posList.clear();
+ String termText = term.getText();
+
+ String nextTermText = documentsModel.seekTerm(termText).map(Term::text).orElse("");
+ term.setText(nextTermText);
+ showedTerm.setText(nextTermText);
+ if (showedTerm.getText().length() > 0) {
+ String num = documentsModel.getDocFreq().map(String::valueOf).orElse("?");
+ termDocsNum.setText(String.format("in %s docs", num));
+
+ term.setEditable(true);
+ firstTermDoc.setDisable(false);
+ } else {
+ nextTerm.setDisable(true);
+ term.setEditable(false);
+ firstTermDoc.setDisable(true);
+ }
+ nextTermDoc.setDisable(true);
+
+ clearStatusMessage();
+ }
+
+ private void showFirstTermDoc() throws LukeException {
+ int doc = documentsModel.firstTermDoc().orElse(-1);
+ if (doc < 0) {
+ nextTermDoc.setDisable(true);
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
+ return;
+ }
+ termDocIdx.setText(String.valueOf(1));
+ docNum.getEditor().setText(String.valueOf(doc));
+ showDoc(doc);
+ List<org.apache.lucene.luke.models.documents.TermPosting> postings = documentsModel.getTermPositions();
+ posList.clear();
+ posList.addAll(
+ postings.stream()
+ .filter(p -> p.getPosition() >= 0)
+ .map(TermPosting::of)
+ .collect(Collectors.toList()));
+
+ nextTermDoc.setDisable(false);
+ clearStatusMessage();
+ }
+
+ private void showNextTermDoc() throws LukeException {
+ int doc = documentsModel.nextTermDoc().orElse(-1);
+ if (doc < 0) {
+ nextTermDoc.setDisable(true);
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
+ return;
+ }
+ int curIdx = Integer.parseInt(termDocIdx.getCharacters().toString());
+ termDocIdx.setText(String.valueOf(curIdx + 1));
+ docNum.getEditor().setText(String.valueOf(doc));
+ showDoc(doc);
+ List<org.apache.lucene.luke.models.documents.TermPosting> postings = documentsModel.getTermPositions();
+ posList.clear();
+ posList.addAll(
+ postings.stream()
+ .filter(p -> p.getPosition() >= 0)
+ .map(TermPosting::of)
+ .collect(Collectors.toList()));
+
+ nextTermDoc.setDisable(false);
+ clearStatusMessage();
+ }
+
+ private Stage addDocumentDialog;
+
+ private void showAddDocumentDialog() throws Exception {
+ addDocumentDialog = new DialogOpener<AddDocumentController>(getParent()).show(
+ addDocumentDialog,
+ "Add Document",
+ "/fxml/dialog/documents/add_document.fxml",
+ 600, 500,
+ (controller) -> {
+ controller.setParent(getParent(), this);
+ controller.setAnalyzer(currentAnalyzer);
+ }
+ );
+ }
+
+ private Stage flagsHelpDialog;
+
+ private void showFlagsHelpDialog() throws Exception {
+ String desc = "Format: IdfpoNPSB#txxVDtxxxxTx/x";
+ ObservableList<String> items = FXCollections.observableArrayList(
+ "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)"
+ );
+ ListView<String> content = new ListView<>(items);
+ flagsHelpDialog = new DialogOpener<HelpController>(getParent()).show(
+ flagsHelpDialog,
+ "About Flags",
+ "/fxml/dialog/help.fxml",
+ 600, 350,
+ (controller) -> {
+ controller.setDescription(desc);
+ controller.setContent(content);
+ }
+ );
+ }
+
+ private ContextMenu createDocTableMenu() {
+ ContextMenu menu = new ContextMenu();
+ // show term vector
+ MenuItem item1 = new MenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item1"));
+ item1.setOnAction(event -> runnableWrapper(() -> {
+ String selectedField = documentTable.getSelectionModel().getSelectedItem().getField();
+ showTermVectorDialog(selectedField);
+ }));
+
+ // show doc values
+ MenuItem item2 = new MenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item2"));
+ item2.setOnAction(event -> runnableWrapper(() -> {
+ DocumentField selected = documentTable.getSelectionModel().getSelectedItem();
+ showDocValuesDialog(selected.getField());
+ }));
+
+ // show stored value
+ MenuItem item3 = new MenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item3"));
+ item3.setOnAction(event -> runnableWrapper(() -> {
+ DocumentField selected = documentTable.getSelectionModel().getSelectedItem();
+ showStoredValueDialog(selected.getField(), selected.getValue());
+ }));
+
+ // copy stored value to clipboard
+ MenuItem item4 = new MenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item4"));
+ item4.setOnAction(event -> {
+ DocumentField selected = documentTable.getSelectionModel().getSelectedItem();
+ copyStoredValue(selected.getField(), selected.getValue());
+ });
+
+ menu.getItems().addAll(item1, item2, item3, item4);
+ return menu;
+ }
+
+ private Stage termVectorDialog = null;
+
+ private void showTermVectorDialog(@Nonnull String field) throws Exception {
+ int docid = Integer.parseInt(showedDocNum.getText());
+ List<TermVectorEntry> tvEntries = documentsModel.getTermVectors(docid, field);
+ if (tvEntries.isEmpty()) {
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.termvector.message.not_available", field, docid));
+ return;
+ }
+
+ termVectorDialog = new DialogOpener<TermVectorController>(getParent()).show(
+ termVectorDialog,
+ "Term Vector",
+ "/fxml/dialog/documents/termvector.fxml",
+ 400, 300,
+ (controller) -> controller.setTermVector(field, tvEntries));
+
+ clearStatusMessage();
+ }
+
+ private Stage docValuesDialog = null;
+
+ private void showDocValuesDialog(@Nonnull String field) throws Exception {
+ int docid = Integer.parseInt(showedDocNum.getText());
+ Optional<DocValues> docValues = documentsModel.getDocValues(docid, field);
+ if (docValues.isPresent()) {
+ docValuesDialog = new DialogOpener<DocValuesController>(getParent()).show(
+ docValuesDialog,
+ "Doc Values",
+ "/fxml/dialog/documents/docvalues.fxml",
+ 400, 300,
+ (controller) -> controller.setValue(field, docValues.get()));
+ clearStatusMessage();
+ } else {
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.docvalues.message.not_available", field, docid));
+ }
+ }
+
+ private Stage storedValueDialog = null;
+
+ private void showStoredValueDialog(@Nonnull String field, @Nullable String stored) throws Exception {
+ if (stored == null || stored.length() == 0) {
+ int docid = Integer.parseInt(showedDocNum.getText());
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
+ return;
+ }
+ storedValueDialog = new DialogOpener<StoredValueController>(getParent()).show(
+ storedValueDialog,
+ "Stored Value",
+ "/fxml/dialog/documents/stored.fxml",
+ 400, 300,
+ (controller) -> controller.setValue(field, stored));
+
+ clearStatusMessage();
+ }
+
+ private void copyStoredValue(@Nonnull String field, @Nullable String stored) {
+ if (stored == null || stored.length() == 0) {
+ int docid = Integer.parseInt(showedDocNum.getText());
+ showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
+ return;
+ }
+
+ Clipboard clipboard = Clipboard.getSystemClipboard();
+ ClipboardContent content = new ClipboardContent();
+ content.putString(stored);
+ clipboard.setContent(content);
+
+ clearStatusMessage();
+ }
+
+ // -------------------------------------------------
+ // methods for interaction with other controllers
+ // -------------------------------------------------
+
+ void browseDocsByTerm(@Nonnull String fieldName, @Nonnull String termText) throws LukeException {
+ field.setValue(fieldName);
+ term.setText(termText);
+ seekTermCeil();
+ showFirstTermDoc();
+ }
+
+ void displayDoc(int docId) {
+ docNum.getEditor().setText(String.valueOf(docId));
+ }
+
+ public void displayLatestDoc() throws LukeException {
+ int docId = documentsModel.getMaxDoc() - 1;
+ docNum.getEditor().setText(String.valueOf(docId));
+ showDoc(docId);
+ }
+
+ void setCurrentAnalyzer(Analyzer analyzer) {
+ this.currentAnalyzer = analyzer;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LogsController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LogsController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LogsController.java
new file mode 100644
index 0000000..f93b259
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LogsController.java
@@ -0,0 +1,43 @@
+/*
+ * 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.controllers;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextArea;
+import org.apache.lucene.luke.app.util.TextAreaAppender;
+
+import java.io.File;
+
+public class LogsController {
+
+ private static final String LOG_FILE = System.getProperty("user.home") + File.separator + ".luke.d" + File.separator + "luke.log";
+
+ @FXML
+ private Label logFile;
+
+ @FXML
+ private TextArea textArea;
+
+ @FXML
+ private void initialize() {
+ logFile.setText(LOG_FILE);
+ TextAreaAppender.textArea = textArea;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LukeController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LukeController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LukeController.java
new file mode 100644
index 0000000..e5df21f
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/LukeController.java
@@ -0,0 +1,304 @@
+/*
+ * 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.controllers;
+
+import com.google.inject.Inject;
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.control.MenuBar;
+import javafx.scene.control.TabPane;
+import javafx.scene.control.Tooltip;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.AnchorPane;
+import javafx.stage.Window;
+import org.apache.lucene.luke.app.DirectoryHandler;
+import org.apache.lucene.luke.app.DirectoryObserver;
+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.Preferences;
+import org.apache.lucene.luke.app.util.MessageUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class LukeController implements IndexObserver, DirectoryObserver {
+
+ private static final Logger logger = LoggerFactory.getLogger(LukeController.class);
+
+ @FXML
+ private AnchorPane primary;
+
+ @FXML
+ private MenuBar menuBar;
+
+ @FXML
+ private TabPane tabPane;
+
+ @FXML
+ private AnchorPane overview;
+
+ @FXML
+ private MenubarController menubarController;
+
+ @FXML
+ private OverviewController overviewController;
+
+ @FXML
+ private DocumentsController documentsController;
+
+ @FXML
+ private SearchController searchController;
+
+ @FXML
+ private AnalysisController analysisController;
+
+ @FXML
+ private CommitsController commitsController;
+
+ @FXML
+ private Label statusMessage;
+
+ @FXML
+ private ImageView multiIcon;
+
+ @FXML
+ private ImageView roIcon;
+
+ @FXML
+ private ImageView noReaderIcon;
+
+ private Preferences prefs;
+
+ private DirectoryHandler directoryHandler;
+
+ private IndexHandler indexHandler;
+
+ @FXML
+ private void initialize() {
+ menubarController.setMainController(this);
+ overviewController.setParent(this);
+ documentsController.setParent(this);
+ searchController.setParent(this);
+ analysisController.setParent(this);
+
+ directoryHandler.addObserver(menubarController);
+ directoryHandler.addObserver(commitsController);
+ directoryHandler.addObserver(this);
+
+ indexHandler.addObserver(menubarController);
+ indexHandler.addObserver(overviewController);
+ indexHandler.addObserver(documentsController);
+ indexHandler.addObserver(searchController);
+ indexHandler.addObserver(commitsController);
+ indexHandler.addObserver(this);
+
+ // disable tabs until an index opened.
+ tabPane.getTabs().get(Tab.OVERVIEW.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.DOCUMENTS.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.SEARCH.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.COMMITS.index()).setDisable(true);
+
+ tabPane.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) ->
+ runnableWrapper(() -> {
+ clearStatusMessage();
+ if (newV.equals(Tab.DOCUMENTS.index())) {
+ documentsController.setCurrentAnalyzer(analysisController.getCurrentAnalyzer());
+ }
+ if (newV.equals(Tab.SEARCH.index())) {
+ searchController.setCurrentAnalyzer(analysisController.getCurrentAnalyzer());
+ }
+ }));
+
+ multiIcon.setVisible(false);
+ Tooltip.install(multiIcon, new Tooltip(MessageUtils.getLocalizedMessage("tooltip.multi_reader")));
+
+ roIcon.setVisible(false);
+ Tooltip.install(roIcon, new Tooltip(MessageUtils.getLocalizedMessage("tooltip.read_only")));
+
+ noReaderIcon.setVisible(false);
+ Tooltip.install(noReaderIcon, new Tooltip(MessageUtils.getLocalizedMessage("tooltip.no_reader")));
+ }
+
+ public void showOpenIndexDialog() throws Exception {
+ menubarController.showOpenIndexDialog();
+ }
+
+ // -------------------------------------------------
+ // methods for interaction with other controllers
+ // -------------------------------------------------
+
+ public Window getPrimaryWindow() {
+ return primary.getScene().getWindow();
+ }
+
+ OverviewController getOverviewController() {
+ return overviewController;
+ }
+
+ DocumentsController getDocumentsController() {
+ return documentsController;
+ }
+
+ SearchController getSearchController() {
+ return searchController;
+ }
+
+ @Override
+ public void openDirectory(LukeState state) {
+ roIcon.setVisible(false);
+ noReaderIcon.setVisible(true);
+
+ tabPane.getTabs().get(Tab.COMMITS.index()).setDisable(false);
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.directory_opened"));
+ }
+
+ @Override
+ public void closeDirectory() {
+ roIcon.setVisible(false);
+ noReaderIcon.setVisible(false);
+
+ switchTab(LukeController.Tab.OVERVIEW);
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.directory_closed"));
+ }
+
+ @Override
+ public void openIndex(LukeState state) {
+
+ if (state.hasDirectoryReader()) {
+ multiIcon.setVisible(false);
+ } else {
+ multiIcon.setVisible(true);
+ }
+
+ if (state.readOnly()) {
+ roIcon.setVisible(true);
+ } else {
+ roIcon.setVisible(false);
+ }
+
+ noReaderIcon.setVisible(false);
+
+ // enable tabs
+ tabPane.getTabs().get(Tab.OVERVIEW.index()).setDisable(false);
+ tabPane.getTabs().get(Tab.DOCUMENTS.index()).setDisable(false);
+ tabPane.getTabs().get(Tab.SEARCH.index()).setDisable(false);
+ if (state.hasDirectoryReader()) {
+ tabPane.getTabs().get(Tab.COMMITS.index()).setDisable(false);
+ }
+
+ // show Overview tab
+ switchTab(LukeController.Tab.OVERVIEW);
+
+ if (state.readOnly()) {
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened_ro"));
+ } else if (!state.hasDirectoryReader()) {
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened_multi"));
+ } else {
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened"));
+ }
+
+ documentsController.setCurrentAnalyzer(analysisController.getCurrentAnalyzer());
+ searchController.setCurrentAnalyzer(analysisController.getCurrentAnalyzer());
+ }
+
+ @Override
+ public void closeIndex() {
+ multiIcon.setVisible(false);
+ roIcon.setVisible(false);
+ noReaderIcon.setVisible(false);
+
+ // disable tabs until index re-opened.
+ tabPane.getTabs().get(Tab.OVERVIEW.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.DOCUMENTS.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.SEARCH.index()).setDisable(true);
+ tabPane.getTabs().get(Tab.COMMITS.index()).setDisable(true);
+
+ switchTab(LukeController.Tab.OVERVIEW);
+ showStatusMessage(MessageUtils.getLocalizedMessage("message.index_closed"));
+ }
+
+ public void switchTab(Tab tab) {
+ tabPane.getSelectionModel().select(tab.index());
+ }
+
+ @Inject
+ public LukeController(Preferences prefs, DirectoryHandler directoryHandler, IndexHandler indexHandler) {
+ this.prefs = prefs;
+ this.directoryHandler = directoryHandler;
+ this.indexHandler = indexHandler;
+ }
+
+ void setColorTheme(ColorTheme colorTheme) {
+ try {
+ prefs.setTheme(colorTheme);
+ } catch (IOException e) {
+ logger.error(e.getMessage(), e);
+ }
+ resetStyles();
+ }
+
+ public void resetStyles() {
+ getPrimaryWindow().getScene().getStylesheets().clear();
+ getPrimaryWindow().getScene().getStylesheets().addAll(
+ getClass().getResource("/styles/luke.css").toExternalForm(),
+ getClass().getResource(prefs.getTheme().resourceName()).toExternalForm()
+ );
+ }
+
+ public String getStyleResourceName() {
+ return prefs.getTheme().resourceName();
+ }
+
+ public void showStatusMessage(String message) {
+ statusMessage.setText(message);
+ }
+
+ public void showUnknownErrorMessage() {
+ statusMessage.setText(MessageUtils.getLocalizedMessage("message.error.unknown"));
+ }
+
+ public void clearStatusMessage() {
+ statusMessage.setText("");
+ }
+
+ public enum Tab {
+ OVERVIEW(0), DOCUMENTS(1), SEARCH(2), ANALYZER(3), COMMITS(4);
+
+ private int tabIdx;
+
+ Tab(int tabIdx) {
+ this.tabIdx = tabIdx;
+ }
+
+ int index() {
+ return tabIdx;
+ }
+ }
+
+ public enum ColorTheme {
+ GRAY, CLASSIC, SANDSTONE, NAVY;
+
+ String resourceName() {
+ return String.format("/styles/theme_%s.css", name().toLowerCase());
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/MenubarController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/MenubarController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/MenubarController.java
new file mode 100644
index 0000000..2f0cb7b
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/MenubarController.java
@@ -0,0 +1,228 @@
+/*
+ * 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.controllers;
+
+import com.google.inject.Inject;
+import javafx.fxml.FXML;
+import javafx.scene.control.MenuItem;
+import javafx.stage.Stage;
+import org.apache.lucene.luke.app.DirectoryHandler;
+import org.apache.lucene.luke.app.DirectoryObserver;
+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.controllers.dialog.menubar.AboutController;
+import org.apache.lucene.luke.app.controllers.dialog.menubar.CheckIndexController;
+import org.apache.lucene.luke.app.controllers.dialog.menubar.OpenIndexController;
+import org.apache.lucene.luke.app.controllers.dialog.menubar.OptimizeController;
+import org.apache.lucene.luke.app.desktop.Preferences;
+import org.apache.lucene.luke.app.util.DialogOpener;
+import org.apache.lucene.luke.models.LukeException;
+import org.apache.lucene.util.Version;
+
+import java.io.IOException;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class MenubarController implements IndexObserver, DirectoryObserver {
+
+ private final Preferences prefs;
+
+ private final DirectoryHandler directoryHandler;
+
+ private final IndexHandler indexHandler;
+
+ private LukeController mainController;
+
+ @FXML
+ private MenuItem menuOpenIndex;
+
+ @FXML
+ private MenuItem menuReopenIndex;
+
+ @FXML
+ private MenuItem menuCloseIndex;
+
+ @FXML
+ private MenuItem menuExit;
+
+ @FXML
+ private MenuItem menuOptimizeIndex;
+
+ @FXML
+ private MenuItem menuCheckIndex;
+
+ @FXML
+ private MenuItem menuThemeGray;
+
+ @FXML
+ private MenuItem menuThemeClassic;
+
+ @FXML
+ private MenuItem menuThemeSandstone;
+
+ @FXML
+ private MenuItem menuThemeNavy;
+
+ @FXML
+ private MenuItem menuAbout;
+
+ @Inject
+ public MenubarController(Preferences prefs, DirectoryHandler directoryHandler, IndexHandler indexHandler) {
+ this.prefs = prefs;
+ this.directoryHandler = directoryHandler;
+ this.indexHandler = indexHandler;
+ }
+
+ @FXML
+ private void initialize() {
+ menuOpenIndex.setOnAction(e -> runnableWrapper(this::showOpenIndexDialog));
+
+ menuReopenIndex.setOnAction(e -> runnableWrapper(() -> {
+ indexHandler.reOpen();
+ }));
+ menuReopenIndex.setDisable(true);
+
+ menuCloseIndex.setOnAction(e -> runnableWrapper(() -> {
+ directoryHandler.close();
+ indexHandler.close();
+ }));
+ menuCloseIndex.setDisable(true);
+
+ menuExit.setOnAction(e -> exit());
+
+ menuOptimizeIndex.setOnAction(e -> runnableWrapper(this::showOptimizeDialog));
+ menuOptimizeIndex.setDisable(true);
+
+ menuCheckIndex.setOnAction(e -> runnableWrapper(this::showCheckIndexDialog));
+ menuCheckIndex.setDisable(true);
+
+ menuThemeGray.setOnAction(e -> runnableWrapper(() -> changeTheme(LukeController.ColorTheme.GRAY)));
+ menuThemeClassic.setOnAction(e -> runnableWrapper(() -> changeTheme(LukeController.ColorTheme.CLASSIC)));
+ menuThemeSandstone.setOnAction(e -> runnableWrapper(() -> changeTheme(LukeController.ColorTheme.SANDSTONE)));
+ menuThemeNavy.setOnAction(e -> runnableWrapper(() -> changeTheme(LukeController.ColorTheme.NAVY)));
+
+ menuAbout.setOnAction(e -> runnableWrapper(this::showAboutDialog));
+ }
+
+ private Stage openIndexDialog = null;
+
+ public void showOpenIndexDialog() throws Exception {
+ openIndexDialog = new DialogOpener<OpenIndexController>(mainController).show(
+ openIndexDialog,
+ "Choose index directory path",
+ "/fxml/dialog/menubar/openindex.fxml",
+ 600, 400,
+ (controller) -> {
+ controller.setMainController(mainController);
+ }
+ );
+ }
+
+ private Stage optimizeDialog = null;
+
+ private void showOptimizeDialog() throws Exception {
+ optimizeDialog = new DialogOpener<OptimizeController>(mainController).show(
+ optimizeDialog,
+ "Optimize index",
+ "/fxml/dialog/menubar/optimize.fxml",
+ 600, 600,
+ (controller) -> {}
+ );
+ }
+
+ private Stage checkIndexDialog = null;
+
+ private void showCheckIndexDialog() throws Exception {
+ checkIndexDialog = new DialogOpener<CheckIndexController>(mainController).show(
+ checkIndexDialog,
+ "Check index",
+ "/fxml/dialog/menubar/checkindex.fxml",
+ 600, 600,
+ (controller) -> {}
+ );
+ }
+
+ private void changeTheme(LukeController.ColorTheme theme) throws IOException {
+ prefs.setTheme(theme);
+ mainController.setColorTheme(theme);
+ }
+
+ private Stage aboutDialog = null;
+
+ private void showAboutDialog() throws Exception {
+ String version = Version.LATEST.toString();
+ aboutDialog = new DialogOpener<AboutController>(mainController).show(
+ aboutDialog,
+ "About Luke v" + version,
+ "/fxml/dialog/menubar/about.fxml",
+ 1000, 480,
+ (controller) -> {},
+ "/styles/about.css"
+ );
+
+ }
+
+ private void exit() {
+ directoryHandler.close();
+ indexHandler.close();
+ ((Stage) mainController.getPrimaryWindow()).close();
+ }
+
+ @Override
+ public void openDirectory(LukeState state) {
+ menuReopenIndex.setDisable(true);
+ menuCloseIndex.setDisable(false);
+ menuOptimizeIndex.setDisable(true);
+ menuCheckIndex.setDisable(false);
+ }
+
+ @Override
+ public void openIndex(LukeState state) throws LukeException {
+ menuReopenIndex.setDisable(false);
+ menuCloseIndex.setDisable(false);
+ if (!state.readOnly() && state.hasDirectoryReader()) {
+ menuOptimizeIndex.setDisable(false);
+ }
+ if (state.hasDirectoryReader()) {
+ menuCheckIndex.setDisable(false);
+ }
+ }
+
+ @Override
+ public void closeDirectory() {
+ close();
+ }
+
+ @Override
+ public void closeIndex() {
+ close();
+ }
+
+ private void close() {
+ menuReopenIndex.setDisable(true);
+ menuCloseIndex.setDisable(true);
+ menuOptimizeIndex.setDisable(true);
+ menuCheckIndex.setDisable(true);
+ }
+
+ void setMainController(LukeController mainController) {
+ this.mainController = mainController;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/OverviewController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/OverviewController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/OverviewController.java
new file mode 100644
index 0000000..66634e9
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/OverviewController.java
@@ -0,0 +1,287 @@
+/*
+ * 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.controllers;
+
+import com.google.inject.Inject;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.ContentDisplay;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Label;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.Spinner;
+import javafx.scene.control.SpinnerValueFactory;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
+import javafx.scene.control.Tooltip;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import org.apache.lucene.luke.app.IndexObserver;
+import org.apache.lucene.luke.app.LukeState;
+import org.apache.lucene.luke.app.controllers.dto.overview.TermCount;
+import org.apache.lucene.luke.app.controllers.dto.overview.TopTerm;
+import org.apache.lucene.luke.app.util.IntegerTextFormatter;
+import org.apache.lucene.luke.models.LukeException;
+import org.apache.lucene.luke.models.overview.Overview;
+import org.apache.lucene.luke.models.overview.OverviewFactory;
+import org.apache.lucene.luke.models.overview.TermCountsOrder;
+import org.apache.lucene.luke.models.overview.TermStats;
+import org.apache.lucene.luke.app.util.MessageUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class OverviewController extends ChildTabController implements IndexObserver {
+
+ private final OverviewFactory overviewFactory;
+
+ private Overview overviewModel;
+
+ @FXML
+ private Label indexPath;
+
+ @FXML
+ private Label mode;
+
+ @FXML
+ private Label numFields;
+
+ @FXML
+ private Label numDocs;
+
+ @FXML
+ private Label numTerms;
+
+ @FXML
+ private Label delOpt;
+
+ @FXML
+ private Label indexVersion;
+
+ @FXML
+ private Label indexFormat;
+
+ @FXML
+ private Label dirImpl;
+
+ @FXML
+ private Label commitPoint;
+
+ @FXML
+ private Label userData;
+
+ @FXML
+ private TableView<TermCount> termCountTable;
+
+ @FXML
+ private TableColumn<TermCount, String> fieldColumn;
+
+ @FXML
+ private TableColumn<TermCount, Long> countColumn;
+
+ @FXML
+ private TableColumn<TermCount, Double> ratioColumn;
+
+ private ObservableList<TermCount> termCountList;
+
+ @FXML
+ private TextField selectedField;
+
+ @FXML
+ private Button showTopTerms;
+
+ @FXML
+ private Spinner<Integer> numTopTerms;
+
+ @FXML
+ private TableView<TopTerm> topTermTable;
+
+ @FXML
+ private TableColumn<TopTerm, Integer> rankColumn;
+
+ @FXML
+ private TableColumn<TopTerm, Integer> freqColumn;
+
+ @FXML
+ private TableColumn<TopTerm, String> textColumn;
+
+ private ObservableList<TopTerm> topTermList;
+
+ @Inject
+ public OverviewController(OverviewFactory overviewFactory) {
+ this.overviewFactory = overviewFactory;
+ }
+
+ @FXML
+ private void initialize() {
+ // initialize term counts table view
+ fieldColumn.setCellValueFactory(new PropertyValueFactory<>("field"));
+ countColumn.setCellValueFactory(new PropertyValueFactory<>("count"));
+ ratioColumn.setCellValueFactory(new PropertyValueFactory<>("ratio"));
+ termCountList = FXCollections.observableArrayList();
+ termCountTable.setItems(termCountList);
+ termCountTable.getSelectionModel().selectedIndexProperty().addListener((obs, oldv, newV) -> {
+ TermCount selected = termCountTable.getSelectionModel().getSelectedItem();
+ if (selected != null) {
+ selectedField.setText(selected.getField());
+ showTopTerms.setDisable(false);
+ }
+ });
+
+ // initialize top terms table view
+ rankColumn.setCellValueFactory(new PropertyValueFactory<>("rank"));
+ freqColumn.setCellValueFactory(new PropertyValueFactory<>("freq"));
+ textColumn.setCellValueFactory(new PropertyValueFactory<>("text"));
+ topTermList = FXCollections.observableArrayList();
+ topTermTable.setItems(topTermList);
+ topTermTable.setContextMenu(createTopTermTableMenu());
+
+ // initialize num of terms spinner
+ SpinnerValueFactory.IntegerSpinnerValueFactory valueFactory =
+ new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1000, 50, -1);
+ numTopTerms.setValueFactory(valueFactory);
+ TextFormatter<Integer> textFormatter = new IntegerTextFormatter(valueFactory.getConverter(), 50);
+ valueFactory.valueProperty().bindBidirectional(textFormatter.valueProperty());
+ numTopTerms.getEditor().setTextFormatter(textFormatter);
+ numTopTerms.focusedProperty().addListener((obs, oldV, newV) -> {
+ if (newV) {
+ // won't not change value, but commit editor
+ // https://stackoverflow.com/questions/32340476/manually-typing-in-text-in-javafx-spinner-is-not-updating-the-value-unless-user
+ numTopTerms.increment(0);
+ }
+ });
+
+ showTopTerms.setOnAction(e -> runnableWrapper(this::onShowTopTerms));
+ }
+
+ @Override
+ public void openIndex(LukeState state) throws LukeException {
+ overviewModel = overviewFactory.newInstance(state.getIndexReader(), state.getIndexPath());
+
+ indexPath.setText(state.getIndexPath());
+ indexPath.setTooltip(new Tooltip(state.getIndexPath()));
+ if (state.readOnly()) {
+ mode.setText("(read-only)");
+ ImageView imageView = new ImageView(new Image("/img/icon_lock.png"));
+ imageView.setFitWidth(12);
+ imageView.setFitHeight(12);
+ mode.setGraphic(imageView);
+ mode.setContentDisplay(ContentDisplay.RIGHT);
+ Tooltip.install(mode, new Tooltip(MessageUtils.getLocalizedMessage("tooltip.read_only")));
+ } else if (!state.hasDirectoryReader()) {
+ mode.setText("(multi-reader)");
+ ImageView imageView = new ImageView(new Image("/img/icon_grid-2x2.png"));
+ imageView.setFitWidth(12);
+ imageView.setFitHeight(12);
+ mode.setGraphic(imageView);
+ mode.setContentDisplay(ContentDisplay.RIGHT);
+ Tooltip.install(mode, new Tooltip(MessageUtils.getLocalizedMessage("tooltip.multi_reader")));
+ } else {
+ mode.setText("");
+ mode.setGraphic(null);
+ }
+ numFields.setText(String.valueOf(overviewModel.getNumFields()));
+ numDocs.setText(String.valueOf(overviewModel.getNumDocuments()));
+ numTerms.setText(String.valueOf(overviewModel.getNumTerms()));
+ String del = overviewModel.hasDeletions() ? String.format("Yes (%d)", overviewModel.getNumDeletedDocs()) : "No";
+ String opt = overviewModel.isOptimized().map(b -> b ? "Yes" : "No").orElse("?");
+ delOpt.setText(String.format("%s / %s", del, opt));
+ indexVersion.setText(overviewModel.getIndexVersion().map(v -> Long.toString(v)).orElse("?"));
+ indexFormat.setText(overviewModel.getIndexFormat().orElse(""));
+ dirImpl.setText(overviewModel.getDirImpl().orElse(""));
+ commitPoint.setText(overviewModel.getCommitDescription().orElse("---"));
+ userData.setText(overviewModel.getCommitUserData().orElse("---"));
+
+ // term counts
+ double numTerms = (double) overviewModel.getNumTerms();
+ Map<String, Long> termCounts = overviewModel.getSortedTermCounts(TermCountsOrder.COUNT_DESC);
+ termCountList.clear();
+ termCountList.addAll(
+ termCounts.entrySet()
+ .stream()
+ .map(e -> TermCount.of(e.getKey(), e.getValue(), numTerms))
+ .collect(Collectors.toList())
+ );
+
+ showTopTerms.setDisable(true);
+ }
+
+ @Override
+ public void closeIndex() {
+ indexPath.setText("");
+ indexPath.setTooltip(null);
+ numFields.setText("");
+ numDocs.setText("");
+ numTerms.setText("");
+ delOpt.setText("");
+ indexVersion.setText("");
+ indexFormat.setText("");
+ dirImpl.setText("");
+ commitPoint.setText("");
+ userData.setText("");
+ termCountList.clear();
+ selectedField.setText("");
+ topTermList.clear();
+ }
+
+ private void onShowTopTerms() throws LukeException {
+ String field = selectedField.getText();
+ Integer numTerms = numTopTerms.getValue();
+ List<TermStats> termStats = overviewModel.getTopTerms(field, numTerms);
+ topTermList.clear();
+ topTermList.addAll(
+ IntStream.range(0, Math.min(numTerms, termStats.size()))
+ .mapToObj(i -> TopTerm.of(i + 1, termStats.get(i)))
+ .collect(Collectors.toList())
+ );
+ clearStatusMessage();
+ }
+
+ private ContextMenu createTopTermTableMenu() {
+ ContextMenu menu = new ContextMenu();
+
+ // browse docs
+ MenuItem item1 = new MenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item1"));
+ item1.setOnAction(e -> runnableWrapper(() -> {
+ TopTerm selected = topTermTable.getSelectionModel().getSelectedItem();
+ getDocumentsController().browseDocsByTerm(selectedField.getText(), selected.getText());
+ switchTab(LukeController.Tab.DOCUMENTS);
+ }));
+
+ // search docs
+ MenuItem item2 = new MenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item2"));
+ item2.setOnAction(e -> runnableWrapper(() -> {
+ TopTerm selected = topTermTable.getSelectionModel().getSelectedItem();
+ getSearchController().searchByTerm(selectedField.getText(), selected.getText());
+ switchTab(LukeController.Tab.SEARCH);
+ }));
+
+ menu.getItems().addAll(item1, item2);
+ return menu;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/SearchController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/SearchController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/SearchController.java
new file mode 100644
index 0000000..ce2581a
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/SearchController.java
@@ -0,0 +1,446 @@
+/*
+ * 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.controllers;
+
+import com.google.common.base.Strings;
+import com.google.inject.Inject;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Accordion;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Label;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextField;
+import javafx.scene.control.TitledPane;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.stage.Stage;
+import javafx.util.converter.IntegerStringConverter;
+import org.apache.lucene.analysis.Analyzer;
+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.controllers.dialog.ConfirmController;
+import org.apache.lucene.luke.app.controllers.dialog.search.ExplanationController;
+import org.apache.lucene.luke.app.controllers.dto.search.SearchResult;
+import org.apache.lucene.luke.app.controllers.fragments.search.AnalyzerController;
+import org.apache.lucene.luke.app.controllers.fragments.search.FieldValuesController;
+import org.apache.lucene.luke.app.controllers.fragments.search.MLTController;
+import org.apache.lucene.luke.app.controllers.fragments.search.QueryParserController;
+import org.apache.lucene.luke.app.controllers.fragments.search.SimilarityController;
+import org.apache.lucene.luke.app.controllers.fragments.search.SortController;
+import org.apache.lucene.luke.app.util.DialogOpener;
+import org.apache.lucene.luke.app.util.IntegerTextFormatter;
+import org.apache.lucene.luke.models.LukeException;
+import org.apache.lucene.luke.models.search.MLTConfig;
+import org.apache.lucene.luke.models.search.QueryParserConfig;
+import org.apache.lucene.luke.models.search.Search;
+import org.apache.lucene.luke.models.search.SearchFactory;
+import org.apache.lucene.luke.models.search.SearchResults;
+import org.apache.lucene.luke.models.search.SimilarityConfig;
+import org.apache.lucene.luke.models.tools.IndexTools;
+import org.apache.lucene.luke.app.util.MessageUtils;
+import org.apache.lucene.luke.models.tools.IndexToolsFactory;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.TermQuery;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class SearchController extends ChildTabController implements IndexObserver {
+
+ private static final int DEFAULT_PAGE_SIZE = 10;
+
+ private final SearchFactory searchFactory;
+
+ private final IndexToolsFactory toolsFactory;
+
+ private final IndexHandler indexHandler;
+
+ private Search searchModel;
+
+ private IndexTools toolsModel;
+
+ private Analyzer curAnalyzer;
+
+ @FXML
+ private Accordion settings;
+
+ @FXML
+ private TitledPane parserPane;
+
+ @FXML
+ private TitledPane mltPane;
+
+ @FXML
+ private ScrollPane parser;
+
+ @FXML
+ private QueryParserController parserController;
+
+ @FXML
+ private ScrollPane analyzer;
+
+ @FXML
+ private AnalyzerController analyzerController;
+
+ @FXML
+ private ScrollPane similarity;
+
+ @FXML
+ private SimilarityController similarityController;
+
+ @FXML
+ private ScrollPane sort;
+
+ @FXML
+ private SortController sortController;
+
+ @FXML
+ private ScrollPane values;
+
+ @FXML
+ private FieldValuesController valuesController;
+
+ @FXML
+ private MLTController mltController;
+
+ @FXML
+ private CheckBox termQuery;
+
+ @FXML
+ private TextArea queryExpr;
+
+ @FXML
+ private TextArea parsedQuery;
+
+ @FXML
+ private Button parseBtn;
+
+ @FXML
+ private CheckBox rewrite;
+
+ @FXML
+ private Button searchBtn;
+
+ @FXML
+ private Button mltBtn;
+
+ @FXML
+ private TextField mltDoc;
+
+ @FXML
+ private Label totalHits;
+
+ @FXML
+ private Label start;
+
+ @FXML
+ private Label end;
+
+ @FXML
+ private Button prev;
+
+ @FXML
+ private Button next;
+
+ @FXML
+ private Button delAll;
+
+ @FXML
+ private TableView<SearchResult> resultsTable;
+
+ @FXML
+ private TableColumn<SearchResult, Integer> docIdColumn;
+
+ @FXML
+ private TableColumn<SearchResult, Float> scoreColumn;
+
+ @FXML
+ private TableColumn<SearchResult, String> valuesColumn;
+
+ private ObservableList<SearchResult> resultList;
+
+ @Inject
+ public SearchController(SearchFactory searchFactory, IndexToolsFactory toolsFactory, IndexHandler indexHandler) {
+ this.searchFactory = searchFactory;
+ this.toolsFactory = toolsFactory;
+ this.indexHandler = indexHandler;
+ }
+
+ @FXML
+ private void initialize() throws LukeException {
+ settings.setExpandedPane(parserPane);
+ parseBtn.setOnAction(e -> runnableWrapper(this::execParse));
+ searchBtn.setOnAction(e -> runnableWrapper(this::execSearch));
+ termQuery.setOnAction(e -> toggleTermQuery());
+
+ mltDoc.setTextFormatter(new IntegerTextFormatter(new IntegerStringConverter(), 0));
+ mltBtn.setOnAction(e -> runnableWrapper(this::execMLTSearch));
+
+ totalHits.setText("0");
+ start.setText("0");
+ end.setText("0");
+
+ next.setDisable(true);
+ next.setOnAction(e -> runnableWrapper(this::nextPage));
+
+ prev.setDisable(true);
+ prev.setOnAction(e -> runnableWrapper(this::prevPage));
+
+ delAll.setDisable(true);
+ delAll.setOnAction(e -> runnableWrapper(this::showDeleteConfirmDialog));
+
+ // initialize results table
+ docIdColumn.setCellValueFactory(new PropertyValueFactory<>("docId"));
+ scoreColumn.setCellValueFactory(new PropertyValueFactory<>("score"));
+ valuesColumn.setCellValueFactory(new PropertyValueFactory<>("values"));
+ resultList = FXCollections.observableArrayList();
+ resultsTable.setItems(resultList);
+ resultsTable.setContextMenu(createResultTableMenu());
+ }
+
+ private void toggleTermQuery() {
+ if (termQuery.isSelected()) {
+ settings.setDisable(true);
+ parseBtn.setDisable(true);
+ rewrite.setDisable(true);
+ mltBtn.setDisable(true);
+ mltDoc.setDisable(true);
+ parsedQuery.setText("");
+ } else {
+ settings.setDisable(false);
+ parseBtn.setDisable(false);
+ rewrite.setDisable(false);
+ mltBtn.setDisable(false);
+ mltDoc.setDisable(false);
+ }
+ }
+
+ @Override
+ public void openIndex(LukeState state) throws LukeException {
+ searchModel = searchFactory.newInstance(state.getIndexReader());
+ toolsModel = toolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
+ sortController.setSearchModel(searchModel);
+
+ queryExpr.setText("*:*");
+ parserController.populateFields(searchModel.getSearchableFieldNames(), searchModel.getRangeSearchableFieldNames());
+ sortController.populateFields(searchModel.getSortableFieldNames());
+ valuesController.populateFields(searchModel.getFieldNames());
+ mltController.populateFields(searchModel.getFieldNames());
+ }
+
+ @Override
+ public void closeIndex() {
+ searchModel = null;
+ toolsModel = null;
+
+ queryExpr.setText("");
+ parsedQuery.setText("");
+ totalHits.setText("0");
+ start.setText("0");
+ end.setText("0");
+ next.setDisable(true);
+ prev.setDisable(true);
+ delAll.setDisable(true);
+ resultList.clear();
+ }
+
+ private void execParse() throws LukeException {
+ Query query = parse(rewrite.isSelected());
+ parsedQuery.setText(query.toString());
+ clearStatusMessage();
+ }
+
+ private void execSearch() throws LukeException {
+ Query query;
+ if (termQuery.isSelected()) {
+ // term query
+ if (Strings.isNullOrEmpty(queryExpr.getText())) {
+ throw new LukeException("Query is not set.");
+ }
+ String[] tmp = queryExpr.getText().split(":");
+ if (tmp.length < 2) {
+ throw new LukeException(String.format("Invalid query [ %s ]", queryExpr.getText()));
+ }
+ query = new TermQuery(new Term(tmp[0].trim(), tmp[1].trim()));
+ } else {
+ query = parse(false);
+ }
+ SimilarityConfig simConfig = similarityController.getConfig();
+ Sort sort = sortController.getSort();
+ Set<String> fieldsToLoad = valuesController.getFieldsToLoad();
+ resultList.clear();
+ SearchResults results = searchModel.search(query, simConfig, sort, fieldsToLoad, DEFAULT_PAGE_SIZE);
+ populateResults(results);
+ }
+
+ private void execMLTSearch() throws LukeException {
+ if (Strings.isNullOrEmpty(mltDoc.getText())) {
+ throw new LukeException("Doc num is not set.");
+ }
+ int docNum = Integer.parseInt(mltDoc.getText());
+ MLTConfig mltConfig = mltController.getMLTConfig();
+
+ Query query = searchModel.mltQuery(docNum, mltConfig, curAnalyzer);
+ Set<String> fieldsToLoad = valuesController.getFieldsToLoad();
+ resultList.clear();
+ SearchResults results = searchModel.search(query, new SimilarityConfig.Builder().build(), fieldsToLoad, DEFAULT_PAGE_SIZE);
+ populateResults(results);
+ }
+
+ private void nextPage() throws LukeException {
+ resultList.clear();
+ searchModel.nextPage().ifPresent(this::populateResults);
+ }
+
+ private void prevPage() throws LukeException {
+ resultList.clear();
+ searchModel.prevPage().ifPresent(this::populateResults);
+ }
+
+ private Query parse(boolean rewrite) throws LukeException {
+ String expr = Strings.isNullOrEmpty(queryExpr.getText()) ? "*:*" : queryExpr.getText();
+ String df = parserController.getDefField();
+ QueryParserConfig config = parserController.getConfig();
+ return searchModel.parseQuery(expr, df, curAnalyzer, config, rewrite);
+ }
+
+ private void populateResults(SearchResults res) {
+ totalHits.setText(String.valueOf(res.getTotalHits()));
+ if (res.getTotalHits() > 0) {
+ start.setText(String.valueOf(res.getOffset() + 1));
+ end.setText(String.valueOf(res.getOffset() + res.size()));
+ resultList.addAll(res.getHits().stream().map(SearchResult::of).collect(Collectors.toList()));
+
+ prev.setDisable(res.getOffset() == 0);
+ next.setDisable(res.getTotalHits() <= res.getOffset() + res.size());
+
+ if (!indexHandler.getState().readOnly() && indexHandler.getState().hasDirectoryReader()) {
+ delAll.setDisable(false);
+ }
+ } else {
+ start.setText("0");
+ end.setText("0");
+ prev.setDisable(true);
+ next.setDisable(true);
+ delAll.setDisable(true);
+ }
+ }
+
+ private Stage confirmDialog;
+
+ private void showDeleteConfirmDialog() throws Exception {
+ confirmDialog = new DialogOpener<ConfirmController>(getParent()).show(
+ confirmDialog,
+ "Confirm Deletion",
+ "/fxml/dialog/confirm.fxml",
+ 400, 200,
+ (controller) -> {
+ controller.setContent(MessageUtils.getLocalizedMessage("search.message.delete_confirm"));
+ controller.setCallback(this::deleteAllDocs);
+ },
+ "/styles/confirm.css"
+ );
+ }
+
+ private void deleteAllDocs() throws LukeException {
+ Query query = searchModel.getCurrentQuery();
+ if (query != null) {
+ toolsModel.deleteDocuments(query);
+ indexHandler.reOpen();
+ showStatusMessage(MessageUtils.getLocalizedMessage("search.message.delete_success", query.toString()));
+ }
+ delAll.setDisable(true);
+ }
+
+ private Stage explanationDialog;
+
+ private ContextMenu createResultTableMenu() {
+ ContextMenu menu = new ContextMenu();
+ MenuItem item1 = new MenuItem(MessageUtils.getLocalizedMessage("search.results.menu.explain"));
+ item1.setOnAction(e -> runnableWrapper(() -> {
+ SearchResult result = resultsTable.getSelectionModel().getSelectedItem();
+ Explanation explanation = searchModel.explain(parse(false), result.getDocId());
+ explanationDialog = new DialogOpener<ExplanationController>(getParent()).show(
+ explanationDialog,
+ "Explanation",
+ "/fxml/dialog/search/explanation.fxml",
+ 600, 400,
+ (controller) -> {
+ controller.setDocNum(result.getDocId());
+ controller.setExplanation(explanation);
+ }
+ );
+ }));
+
+ MenuItem item2 = new MenuItem(MessageUtils.getLocalizedMessage("search.results.menu.showdoc"));
+ item2.setOnAction(e -> runnableWrapper(() -> {
+ SearchResult result = resultsTable.getSelectionModel().getSelectedItem();
+ getDocumentsController().displayDoc(result.getDocId());
+ switchTab(LukeController.Tab.DOCUMENTS);
+ }));
+
+ menu.getItems().addAll(item1, item2);
+ return menu;
+ }
+
+ @Override
+ public void setParent(LukeController parent) {
+ super.setParent(parent);
+ analyzerController.setParent(parent);
+ mltController.setParent(parent);
+ }
+
+ // -------------------------------------------------
+ // methods for interaction with other controllers
+ // -------------------------------------------------
+
+ void setCurrentAnalyzer(Analyzer analyzer) {
+ this.curAnalyzer = analyzer;
+ analyzerController.setCurrentAnalyzer(analyzer);
+ mltController.setCurrentAnalyzer(analyzer);
+ }
+
+ void searchByTerm(@Nonnull String fieldName, @Nonnull String termText) throws LukeException {
+ termQuery.selectedProperty().setValue(true);
+ toggleTermQuery();
+ queryExpr.setText(String.format("%s:%s", fieldName, termText));
+ resultList.clear();
+ execSearch();
+ }
+
+ void mltSearch(int docNum) throws LukeException {
+ mltDoc.setText(String.valueOf(docNum));
+ settings.setExpandedPane(mltPane);
+ execMLTSearch();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/ConfirmController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/ConfirmController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/ConfirmController.java
new file mode 100644
index 0000000..887944b
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/ConfirmController.java
@@ -0,0 +1,62 @@
+/*
+ * 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.controllers.dialog;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import org.apache.lucene.luke.models.LukeException;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class ConfirmController implements DialogWindowController {
+
+ @FXML
+ private Label message;
+
+ @FXML
+ private Button ok;
+
+ @FXML
+ private Button cancel;
+
+ @FXML
+ private void initialize() {
+ ok.setOnAction(e -> runnableWrapper(() -> {
+ callback.exec();
+ closeWindow(ok);
+ }));
+ cancel.setOnAction(e -> closeWindow(cancel));
+ }
+
+ public void setContent(String content) {
+ message.setText(content);
+ }
+
+ private Callback callback = () -> {
+ };
+
+ public void setCallback(Callback callback) {
+ this.callback = callback;
+ }
+
+ @FunctionalInterface
+ public interface Callback {
+ void exec() throws LukeException;
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/DialogWindowController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/DialogWindowController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/DialogWindowController.java
new file mode 100644
index 0000000..ad3a8e9
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/DialogWindowController.java
@@ -0,0 +1,30 @@
+/*
+ * 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.controllers.dialog;
+
+import javafx.scene.Node;
+import javafx.stage.Stage;
+
+public interface DialogWindowController {
+
+ default void closeWindow(Node node) {
+ Stage stage = (Stage) node.getScene().getWindow();
+ stage.close();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/HelpController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/HelpController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/HelpController.java
new file mode 100644
index 0000000..8856994
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/HelpController.java
@@ -0,0 +1,53 @@
+/*
+ * 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.controllers.dialog;
+
+import javafx.fxml.FXML;
+import javafx.scene.Node;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.layout.AnchorPane;
+
+public class HelpController implements DialogWindowController {
+
+ @FXML
+ private Label description;
+
+ @FXML
+ private AnchorPane content;
+
+ @FXML
+ private Button close;
+
+ @FXML
+ private void initialize() {
+ close.setOnAction(e -> closeWindow(close));
+ }
+
+ public void setDescription(String desc) {
+ description.setText(desc);
+ }
+
+ public void setContent(Node child) {
+ AnchorPane.setTopAnchor(child, 0.0);
+ AnchorPane.setBottomAnchor(child, 0.0);
+ AnchorPane.setLeftAnchor(child, 0.0);
+ AnchorPane.setRightAnchor(child, 0.0);
+ content.getChildren().add(child);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/WarnController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/WarnController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/WarnController.java
new file mode 100644
index 0000000..f633eff
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/WarnController.java
@@ -0,0 +1,41 @@
+/*
+ * 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.controllers.dialog;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+
+public class WarnController implements DialogWindowController {
+
+ @FXML
+ private Label message;
+
+ @FXML
+ private Button close;
+
+ @FXML
+ private void initialize() {
+ close.setOnAction(e -> closeWindow(close));
+ }
+
+ public void setContent(String content) {
+ message.setText(content);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditFiltersController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditFiltersController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditFiltersController.java
new file mode 100644
index 0000000..e35e952
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditFiltersController.java
@@ -0,0 +1,137 @@
+/*
+ * 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.controllers.dialog.analysis;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.CheckBoxTableCell;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.input.MouseButton;
+import org.apache.lucene.luke.app.controllers.dialog.DialogWindowController;
+import org.apache.lucene.luke.app.controllers.dto.analysis.FilterFactory;
+import org.apache.lucene.luke.app.controllers.fragments.analysis.CustomAnalyzerController;
+
+import java.util.List;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+import static org.apache.lucene.luke.app.util.ExceptionHandler.runnableWrapper;
+
+public class EditFiltersController implements DialogWindowController {
+
+ private CustomAnalyzerController customAnalyzerController;
+
+ private Mode mode;
+
+ private Consumer<List<FilterFactory>> callback;
+
+ @FXML
+ private TableView<FilterFactory> filtersTable;
+
+ @FXML
+ private TableColumn<FilterFactory, Boolean> delColumn;
+
+ @FXML
+ private TableColumn<FilterFactory, Integer> orderColumn;
+
+ @FXML
+ private TableColumn<FilterFactory, String> factoryColumn;
+
+ private ObservableList<FilterFactory> filterList;
+
+ @FXML
+ private Button ok;
+
+ @FXML
+ private Button cancel;
+
+ @FXML
+ private void initialize() {
+ delColumn.setCellValueFactory(data -> {
+ BooleanProperty prop = data.getValue().getDeletedProperty();
+ prop.addListener((obs, oldV, newV) ->
+ filtersTable.getSelectionModel().getSelectedItem().setDeleted(newV)
+ );
+ return prop;
+ });
+ delColumn.setCellFactory(col -> {
+ CheckBoxTableCell<FilterFactory, Boolean> cell = new CheckBoxTableCell<>();
+ cell.setAlignment(Pos.CENTER);
+ return cell;
+ });
+ orderColumn.setCellValueFactory(new PropertyValueFactory<>("order"));
+ factoryColumn.setCellValueFactory(new PropertyValueFactory<>("factory"));
+ filterList = FXCollections.observableArrayList();
+ filtersTable.setItems(filterList);
+ filtersTable.setOnMouseClicked(e -> runnableWrapper(() -> {
+ if (e.getButton().equals(MouseButton.PRIMARY) &&
+ e.getClickCount() == 2) {
+ editParams();
+ }
+ }));
+
+ ok.setOnAction(e -> onOk());
+ cancel.setOnAction(e -> closeWindow(cancel));
+ }
+
+ private void editParams() throws Exception {
+ int selectedIdx = filtersTable.getSelectionModel().getFocusedIndex();
+ switch (mode) {
+ case CHAR_FILTER:
+ customAnalyzerController.editCharFilterParams(selectedIdx);
+ break;
+ case TOKEN_FILTER:
+ customAnalyzerController.editTokenFilterParams(selectedIdx);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void onOk() {
+ callback.accept(filterList);
+ closeWindow(ok);
+ }
+
+ public void populate(List<String> filters) {
+ List<FilterFactory> li = IntStream.range(0, filters.size())
+ .mapToObj(i -> FilterFactory.of(i + 1, filters.get(i)))
+ .collect(Collectors.toList());
+ filterList.addAll(li);
+ }
+
+ public void setCallback(Consumer<List<FilterFactory>> callback) {
+ this.callback = callback;
+ }
+
+ public void setParent(CustomAnalyzerController customAnalyzerController, Mode mode) {
+ this.customAnalyzerController = customAnalyzerController;
+ this.mode = mode;
+ }
+
+ public enum Mode {
+ CHAR_FILTER, TOKEN_FILTER
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditParamsController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditParamsController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditParamsController.java
new file mode 100644
index 0000000..be6a167
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/EditParamsController.java
@@ -0,0 +1,134 @@
+/*
+ * 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.controllers.dialog.analysis;
+
+import javafx.beans.property.BooleanProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Pos;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.CheckBoxTableCell;
+import javafx.scene.control.cell.PropertyValueFactory;
+import javafx.scene.control.cell.TextFieldTableCell;
+import org.apache.lucene.luke.app.controllers.dialog.DialogWindowController;
+import org.apache.lucene.luke.app.controllers.dto.analysis.Param;
+
+import javax.annotation.Nullable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+public class EditParamsController implements DialogWindowController {
+
+ private Consumer<List<Param>> callback;
+
+ @FXML
+ private Label target;
+
+ @FXML
+ private TableView<Param> paramsTable;
+
+ @FXML
+ private TableColumn<Param, Boolean> delColumn;
+
+ @FXML
+ private TableColumn<Param, String> nameColumn;
+
+ @FXML
+ private TableColumn<Param, String> valueColumn;
+
+ private ObservableList<Param> paramList;
+
+ @FXML
+ private Button ok;
+
+ @FXML
+ private Button cancel;
+
+ @FXML
+ private void initialize() {
+ delColumn.setCellValueFactory(data -> {
+ BooleanProperty prop = data.getValue().getDeletedProperty();
+ prop.addListener((obs, oldV, newV) ->
+ paramsTable.getSelectionModel().getSelectedItem().setDeleted(newV)
+ );
+ return prop;
+ });
+ delColumn.setCellFactory(col -> {
+ CheckBoxTableCell<Param, Boolean> cell = new CheckBoxTableCell<>();
+ cell.setAlignment(Pos.CENTER);
+ return cell;
+ });
+
+ nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
+ nameColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ nameColumn.setOnEditCommit(e -> {
+ int rowIdx = paramsTable.getSelectionModel().getFocusedIndex();
+ paramList.get(rowIdx).setName(e.getNewValue());
+ });
+
+ valueColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
+ valueColumn.setCellFactory(TextFieldTableCell.forTableColumn());
+ valueColumn.setOnEditCommit(e -> {
+ int rowIdx = paramsTable.getSelectionModel().getFocusedIndex();
+ paramList.get(rowIdx).setValue(e.getNewValue());
+ });
+
+ paramList = FXCollections.observableArrayList();
+ paramsTable.setItems(paramList);
+
+ ok.setOnAction(e -> onOk());
+ cancel.setOnAction(e -> closeWindow(cancel));
+ }
+
+ private void onOk() {
+ callback.accept(paramList);
+ closeWindow(ok);
+ }
+
+ public void setTarget(String targetName) {
+ this.target.setText(targetName);
+ }
+
+ public void setCallback(Consumer<List<Param>> callback) {
+ this.callback = callback;
+ }
+
+ public void populate(@Nullable Map<String, String> paramMap) {
+ List<Param> li;
+ if (paramMap != null) {
+ li = new ArrayList<>();
+ for (Map.Entry<String, String> entry : paramMap.entrySet()) {
+ li.add(Param.of(entry.getKey(), entry.getValue()));
+ }
+ li.addAll(IntStream.range(0, Math.max(10 - paramMap.size(), 0))
+ .mapToObj(i -> Param.newInstance())
+ .collect(Collectors.toList()));
+ } else {
+ li = IntStream.range(0, 10).mapToObj(i -> Param.newInstance()).collect(Collectors.toList());
+ }
+ paramList.addAll(li);
+ }
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/dc72c4a0/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/TokenAttributeController.java
----------------------------------------------------------------------
diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/TokenAttributeController.java b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/TokenAttributeController.java
new file mode 100644
index 0000000..d6e7422
--- /dev/null
+++ b/lucene/luke/src/java/org/apache/lucene/luke/app/controllers/dialog/analysis/TokenAttributeController.java
@@ -0,0 +1,75 @@
+/*
+ * 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.controllers.dialog.analysis;
+
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.TableColumn;
+import javafx.scene.control.TableView;
+import javafx.scene.control.cell.PropertyValueFactory;
+import org.apache.lucene.luke.app.controllers.dialog.DialogWindowController;
+import org.apache.lucene.luke.app.controllers.dto.analysis.TokenAttValue;
+import org.apache.lucene.luke.models.analysis.Analysis;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class TokenAttributeController implements DialogWindowController {
+
+ @FXML
+ private Label term;
+
+ @FXML
+ private TableView<TokenAttValue> attTable;
+
+ @FXML
+ private TableColumn<TokenAttValue, String> attClassColumn;
+
+ @FXML
+ private TableColumn<TokenAttValue, String> nameColumn;
+
+ @FXML
+ private TableColumn<TokenAttValue, String> valueColumn;
+
+ private ObservableList<TokenAttValue> attList;
+
+ @FXML
+ private Button ok;
+
+ @FXML
+ private void initialize() {
+ attClassColumn.setCellValueFactory(new PropertyValueFactory<>("attClass"));
+ nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
+ valueColumn.setCellValueFactory(new PropertyValueFactory<>("value"));
+ attList = FXCollections.observableArrayList();
+ attTable.setItems(attList);
+
+ ok.setOnAction(e -> closeWindow(ok));
+ }
+
+ public void populate(String termText, List<Analysis.TokenAttribute> attributes) {
+ term.setText(termText);
+ attList.addAll(attributes.stream()
+ .flatMap(att -> att.getAttValues().entrySet().stream()
+ .map(e -> TokenAttValue.of(att.getAttClass(), e.getKey(), e.getValue())
+ )).collect(Collectors.toList()));
+ }
+}