package mj.ocraptor.javafx.controllers;
// {{{
import java.io.File;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.chart.PieChart;
import javafx.scene.chart.PieChart.Data;
import javafx.scene.control.Button;
import javafx.scene.control.Hyperlink;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.DirectoryChooser;
import javafx.util.Callback;
import mj.ocraptor.MainController;
import mj.ocraptor.configuration.Config;
import mj.ocraptor.configuration.properties.ConfigInteger;
import mj.ocraptor.configuration.properties.ConfigString;
import mj.ocraptor.database.DBManager;
import mj.ocraptor.database.dao.ResultError;
import mj.ocraptor.database.error.TableEmptyException;
import mj.ocraptor.javafx.DoughnutChart;
import mj.ocraptor.javafx.GUITemplate;
import mj.ocraptor.javafx.Icon;
import mj.ocraptor.tools.DataStructureTools;
import mj.ocraptor.tools.St;
import mj.ocraptor.tools.SystemTools;
// }}}
public class EditDatabase extends GUITemplate {
// {{{
// *INDENT-OFF*
public static double INIT_WIDTH = 550;
public static double INIT_HEIGHT = 235;
public static final String FXML = "EditDatabase.fxml";
// *INDENT-ON*
private DBManager db;
private final Logger LOG = LoggerFactory.getLogger(getClass());
private boolean showResetDBMessage = true;
private static final int MIN_EXPECTED_RAM_IN_MB = 2048;
private static final int MIN_RAM_TO_KEEP_FREE_IN_MB = 512;
private static final Integer INFO_SCREEN_WIDTH = 550;
private static final Integer INFO_SCREEN_HEIGTH = 600;
// }}}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
// {{{
@FXML
private Button deleteButton;
@FXML
private Button searchButton;
@FXML
private Button configButton;
@FXML
private Button backButton;
@FXML
private Button updateButton;
@FXML
private Label emptyMessage;
@FXML
private Button infoButton;
@FXML
private Button addFolderButton;
@FXML
private VBox emptyMessageBox;
@FXML
private ListView<String> folderList = new ListView<String>();
// }}}
@FXML
private void deleteButtonClicked(ActionEvent event) {
EventHandler<ActionEvent> handler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
File configFile = new File(Config.inst().getConfigUserFilePath());
if (!configFile.getAbsoluteFile().equals(new File(cfg.getConfigMasterFilePath())
.getAbsoluteFile())) {
final File database = new File(cfg.getProp(configFile.getAbsolutePath(),
ConfigString.DATABASE_FOLDER));
if (database.exists()) {
FileUtils.deleteQuietly(database);
}
FileUtils.deleteQuietly(configFile);
try {
cfg.setConfigUserFilePath(cfg.getConfigMasterFilePath());
} catch (Exception ex) {
// TODO: logging
ex.printStackTrace();
}
gotoPage(SelectDatabase.FXML, SelectDatabase.INIT_WIDTH, SelectDatabase.INIT_HEIGHT);
}
}
};
this.g.showConfirmationDialog(g.getText("EDIT_DB.DELETE_DB_CONFIRMATION"), handler);
}
@FXML
private void updateButtonClicked(ActionEvent event) {
// ------------------------------------------------ //
EventHandler<ActionEvent> finalYesHandler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
try {
db.reset();
try {
db.databaseIncomplete();
} catch (Exception ex) {
gotoPage(LoadingScreen.FXML, LoadingScreen.INIT_WIDTH, LoadingScreen.INIT_HEIGHT);
}
} catch (Exception ex) {
// TODO: logging
ex.printStackTrace();
}
}
};
// ------------------------------------------------ //
EventHandler<ActionEvent> yesHandler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
g.showYesNoDialog(g.getText("EDIT_DB.SECOND_CONFIRMATION"), finalYesHandler, null, false);
}
};
EventHandler<ActionEvent> noHandler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
try {
try {
db.databaseIncomplete();
gotoPage(LoadingScreen.FXML, LoadingScreen.INIT_WIDTH, LoadingScreen.INIT_HEIGHT);
} catch (Exception ex) {
ex.printStackTrace();
}
} catch (Exception ex) {
// TODO: logging
ex.printStackTrace();
}
}
};
// ------------------------------------------------ //
EventHandler<ActionEvent> ramYesHandler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
try {
if (showResetDBMessage) {
g.showYesNoDialog(g.getText("EDIT_DB.RESET_DATABASE_BEFORE_INDEXING"), yesHandler,
noHandler, true);
} else {
finalYesHandler.handle(null);
}
} catch (Exception ex) {
// TODO: logging
ex.printStackTrace();
}
}
};
// ------------------------------------------------ //
try {
final SystemTools sigar = new SystemTools();
final long maxClientRam = sigar.getMaxRamInMB();
final long freeRam = sigar.getFreeRamInMB();
final Integer xmxValue = cfg.getProp(ConfigInteger.PROCESS_XMX);
String ramError = null;
if (maxClientRam < MIN_EXPECTED_RAM_IN_MB) {
ramError = g.getText("EDIT_DB.MAX_RAM_TOO_LOW", MIN_EXPECTED_RAM_IN_MB, maxClientRam);
} else if (freeRam < (xmxValue + MIN_RAM_TO_KEEP_FREE_IN_MB)) {
ramError = g.getText("EDIT_DB.FREE_RAM_TOO_LOW", xmxValue + MIN_RAM_TO_KEEP_FREE_IN_MB,
freeRam);
}
if (ramError != null) {
this.g.showYesNoDialog(ramError, ramYesHandler, null, false, 500, 150);
return;
}
} catch (Exception e) {
// TODO: logging
e.printStackTrace();
}
if (this.showResetDBMessage) {
this.g.showYesNoDialog(g.getText("EDIT_DB.RESET_DATABASE_BEFORE_INDEXING"), yesHandler,
noHandler, true);
} else {
finalYesHandler.handle(null);
}
}
@FXML
void addFolderButtonClicked(ActionEvent event) {
DirectoryChooser fileChooser = new DirectoryChooser();
fileChooser.setTitle(g.getText("EDIT_DB.DIRECTORY_CHOOSER_TITLE"));
File file = fileChooser.showDialog(this.g.getPrimaryStage());
if (file != null) {
Config.inst().addFolderToIndex(file);
this.updateFolderList();
}
}
@FXML
private void searchButtonClicked(ActionEvent event) {
this.gotoPage(SearchDialog.FXML, SearchDialog.INIT_WIDTH, SearchDialog.INIT_HEIGHT);
}
/**
*
*
* @param event
*/
@FXML
void infoButtonClicked(ActionEvent event) {
final DBManager db = MainController.inst().getDb();
Integer entryCount = null;
final List<Node> infoScreen = new ArrayList<Node>();
Connection connection = null;
try {
connection = db.getConnection();
entryCount = db.countEntries(connection);
Text notFinished = new Text(g.getText("EDIT_DB.NOT_FINISHED_YET") + "\n\n");
notFinished.setFill(Color.DARKRED);
infoScreen.add(notFinished);
Text countText = new Text(St.zeroPadSpaces(entryCount, 5) + " " + g.getText(
"EDIT_DB.DATABASE_ENTRY_COUNT") + "\n");
countText.setFont(Font.font(java.awt.Font.MONOSPACED, 13));
infoScreen.add(countText);
// {{{ Show file size
Long fileSizeInMB = FileUtils.sizeOfDirectory(new File(this.cfg.getDatabasePath())) / 1024
/ 1024;
if (fileSizeInMB == 0) {
fileSizeInMB = 1L;
}
Text sizeText = new Text(St.zeroPadSpaces(DataStructureTools.safeLongToInt(fileSizeInMB), 5)
+ " " + g.getText("EDIT_DB.SIZE_OCCUPIED") + "\n");
sizeText.setFont(Font.font(java.awt.Font.MONOSPACED, 13));
infoScreen.add(sizeText);
// }}}
// {{{ show a list of saved error codes
for (final ResultError possibleError : ResultError.values()) {
Integer errorCount = db.countErrors(connection, possibleError);
// TODO: errorcount must be > 0
if (errorCount != null && errorCount > 0) {
final Text errorCountText = new Text(St.zeroPadSpaces(errorCount, 5) + " "
+ getExplanation(possibleError) + " - ");
errorCountText.setFont(Font.font(java.awt.Font.MONOSPACED, 13));
infoScreen.add(errorCountText);
final Hyperlink searchHyperLink = new Hyperlink(g.getText("EDIT_DB.INFO_SHOW_ME"));
searchHyperLink.setFont(Font.font(java.awt.Font.MONOSPACED, 13));
searchHyperLink.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
g.setLastContentSearch(possibleError.getErrorCode());
searchButtonClicked(null);
}
});
infoScreen.add(searchHyperLink);
infoScreen.add(new Text("\n"));
}
}
// }}}
// {{{ Show pie chart for indexed file types
Map<String, Integer> extensions = db.countExtensions(connection);
if (extensions.size() > 2 && entryCount > 10) {
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
final ObservableList<PieChart.Data> extensionsChartData = FXCollections
.observableArrayList();
final DoughnutChart extensionsChart = new DoughnutChart(extensionsChartData);
for (final String extension : extensions.keySet()) {
int extensionCount = extensions.get(extension);
final String percentage = St.formatDouble((((double) extensionCount / (double) entryCount)
* 100), 1);
extensionsChartData.add(new PieChart.Data(extension + " (" + percentage + "%)",
extensionCount));
}
extensionsChart.setPrefWidth(INFO_SCREEN_WIDTH);
Collections.sort(extensionsChartData, new Comparator<PieChart.Data>() {
@Override
public int compare(Data o1, Data o2) {
Number value1 = (Number) o1.getPieValue();
Number value2 = (Number) o2.getPieValue();
return new BigDecimal(value1.toString()).compareTo(new BigDecimal(value2.toString()));
}
});
extensionsChart.setTitle(g.getText("EDIT_DB.EXTENSIONS"));
extensionsChart.setLegendVisible(false);
infoScreen.add(new Text("\n\n"));
vbox.getChildren().add(extensionsChart);
infoScreen.add(vbox);
}
// }}}
} catch (Exception e) {
} finally {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
}
}
}
this.g.showMessage(INFO_SCREEN_WIDTH, INFO_SCREEN_HEIGTH, 0, g.getText("EDIT_DB.DATABASE_INFO"),
Color.BLACK, true, infoScreen.toArray(new Node[infoScreen.size()]));
}
/**
*
*
* @param error
* @return
*/
private String getExplanation(final ResultError error) {
if (error == ResultError.TIMEOUT) {
return g.getText("EDIT_DB.INFO_TIMEOUT_ERROR");
} else if (error == ResultError.PARSING) {
return g.getText("EDIT_DB.INFO_UNKNOWN_ERROR");
} else if (error == ResultError.OCR) {
return g.getText("EDIT_DB.OCR_ERROR");
}
return g.getText("EDIT_DB.INFO_UNDEFINED_ERROR");
}
@FXML
private void configButtonClicked(ActionEvent event) {
this.gotoPage(SettingsManager.FXML, SettingsManager.INIT_WIDTH, SettingsManager.INIT_HEIGHT);
}
@FXML
private void backButtonClicked(ActionEvent event) {
try {
this.cfg.setConfigUserFilePath(this.cfg.getConfigMasterFilePath());
} catch (Exception e) {
// TODO: logging
e.printStackTrace();
}
this.gotoPage(SelectDatabase.FXML, SelectDatabase.INIT_WIDTH, SelectDatabase.INIT_HEIGHT);
}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
@Override
protected void initVisibility() {
this.helpButton.setDisable(false);
this.emptyMessageBox.setManaged(true);
this.emptyMessageBox.setVisible(true);
this.infoButton.setDisable(true);
this.updateButton.setDisable(true);
this.searchButton.setDisable(true);
}
@Override
protected void initLabels() {
final String dbPath = St.getFileNameWithoutExtension(Config.inst().getConfigUserFilePath());
this.title.setText(g.getText("EDIT_DB.TITLE", dbPath));
this.emptyMessage.setText(g.getText("EDIT_DB.DROP_HERE"));
this.emptyMessage.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent t) {
addFolderButtonClicked(null);
}
});
// *INDENT-OFF*
this.addFolderButton.setText( g.getText("EDIT_DB.ADD_FOLDER_BUTTON"));
this.updateButton.setText ( g.getText("EDIT_DB.INDEX_BUTTON"));
this.infoButton.setText ( g.getText("EDIT_DB.INFO_BUTTON"));
this.searchButton.setText ( g.getText("EDIT_DB.SEARCH_BUTTON"));
this.backButton.setText ( g.getText("BACK_BUTTON"));
this.deleteButton.setText ( g.getText("DELETE"));
this.configButton.setText ( g.getText("EDIT_DB.CONFIG"));
this.helpButton.setText ( g.getText("HELP"));
// *INDENT-ON*
}
@Override
public void initCustomComponents() {
// this.g.setLastContentSearch(null);
this.db = this.g.getParentController().initDatabase();
this.updateFolderList();
// *INDENT-OFF*
this.addTooltip(this.infoButton, g.getText("EDIT_DB.INDEXED_FILE_INFOS"), -50, 50);
this.addTooltip(this.updateButton, g.getText("EDIT_DB.START_INDEXING_TOOLTIP"), -50, 50);
this.addTooltip(this.configButton, g.getText("EDIT_DB.CONFIG_TOOLTIP"), -50, -80);
this.addTooltip(this.deleteButton, g.getText("EDIT_DB.DELETE_BUTTON_TOOLTIP"), -50, -70);
this.addTooltip(this.searchButton, g.getText("EDIT_DB.SEARCH_BUTTON_TOOLTIP"), -50, 50);
// *INDENT-ON*
// touch config file
this.cfg.setProp(ConfigString.LAST_TIME_USED, String.valueOf(new Date().getTime()));
this.pane.getScene().addEventHandler(KeyEvent.ANY, new EventHandler<KeyEvent>() {
@Override
public void handle(KeyEvent event) {
if (event.getCode() == KeyCode.ENTER || event.getCode() == KeyCode.S) {
if (!searchButton.isDisabled()) {
searchButtonClicked(null);
}
}
if (event.getCode() == KeyCode.ESCAPE) {
backButtonClicked(null);
}
}
});
this.pane.getScene().setOnDragOver(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
if (db.hasFiles()) {
event.acceptTransferModes(TransferMode.COPY);
} else {
event.consume();
}
}
});
this.pane.getScene().setOnDragDropped(new EventHandler<DragEvent>() {
@Override
public void handle(DragEvent event) {
Dragboard db = event.getDragboard();
boolean success = false;
if (db.hasFiles()) {
success = true;
List<File> acceptedFiles = new ArrayList<File>();
for (File file : db.getFiles()) {
if (file.exists() && file.isDirectory() && file.canRead()) {
acceptedFiles.add(file);
}
}
for (File file : acceptedFiles) {
Config.inst().addFolderToIndex(file);
}
updateFolderList();
}
event.setDropCompleted(success);
event.consume();
}
});
this.executeWorker(propValidatingWorker());
}
@Override
protected void initListeners() {
}
@Override
protected void asserts() {
// TODO: javafx asserts
}
/**
*
*/
private class RemoveDirectoryButton extends ListCell<String> {
HBox hbox = new HBox();
Label label = new Label("");
Pane pane = new Pane();
Button button = new Button("");
String lastItem;
public RemoveDirectoryButton() {
super();
Insets padding = new Insets(0, 0, 0, 10);
addImageIcon(button, Icon.TRASH_WHITE, 1);
addTooltip(button, g.getText("EDIT_DB.REMOVE_DIR_TOOLTIP"), 50, 0);
button.getStyleClass().add("removeDirectoryButton");
button.setMinWidth(40);
button.setMaxWidth(40);
button.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
EventHandler<ActionEvent> handler = new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
if (lastItem != null) {
Config.inst().removeFolderToIndex(St.resolveFilePath(lastItem));
updateFolderList();
}
}
};
g.showConfirmationDialog(g.getText("EDIT_DB.REMOVE_DIR_CONFIRMATION"), handler);
}
});
label.setPadding(padding);
hbox.getChildren().addAll(button, label);
hbox.setAlignment(Pos.CENTER_LEFT);
HBox.setHgrow(pane, Priority.ALWAYS);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(null);
if (empty) {
lastItem = null;
setGraphic(null);
} else {
lastItem = item;
label.setText(item != null ? item : "<null>");
if (item != null) {
File directory = new File(St.resolveFilePath(item));
if (!directory.exists()) {
// TODO: style
label.setTextFill(Color.RED);
addTooltip(label, g.getText("EDIT_DB.DIR_DOES_NOT_EXIST"), 50, 30);
} else if (!directory.canRead()) {
label.setTextFill(Color.ORANGE);
addTooltip(label, g.getText("EDIT_DB.DIR_NOT_READABLE"), 50, 30);
} else {
label.setTextFill(Color.BLACK);
}
}
setGraphic(hbox);
}
}
}
@Override
protected double getWindowWidth() {
return INIT_WIDTH;
}
@Override
protected double getWindowHeight() {
return INIT_HEIGHT;
}
/**
*
*
*/
private void updateFolderList() {
try {
// try {
// this.db.printTables(true);
// } catch (Exception e) {
// }
ArrayList<String> folders = Config.inst().getFoldersToIndex();
String[] folderStrings = new String[folders.size()];
boolean allDirectoriesExist = true;
for (int i = 0; i < folders.size(); i++) {
final String folderPath = folders.get(i);
final File folderFile = new File(folderPath);
folderStrings[i] = St.shortenHomePathInDirectory(folderPath);
if (!folderFile.exists() || !folderFile.canRead()) {
allDirectoriesExist = false;
}
}
ObservableList<String> layerList = FXCollections.observableArrayList(folderStrings);
folderList.setItems(layerList);
folderList.setCellFactory(new Callback<ListView<String>, ListCell<String>>() {
@Override
public ListCell<String> call(ListView<String> param) {
return new RemoveDirectoryButton();
}
});
boolean empty = layerList.isEmpty();
this.emptyMessageBox.setManaged(empty);
this.emptyMessageBox.setVisible(empty);
this.updateButton.setDisable(empty);
String date = this.cfg.getProp(ConfigString.LAST_TIME_MODIFIED);
boolean incompleteDB = false;
try {
this.db.databaseIncomplete();
} catch (Exception e) {
incompleteDB = true;
}
boolean searchAvailable = empty || date.isEmpty() || incompleteDB || !allDirectoriesExist;
this.searchButton.setDisable(searchAvailable);
this.infoButton.setDisable(searchAvailable);
this.showResetDBMessage = !searchAvailable;
} catch (Exception e) {
LOG.error("...", e);
backButtonClicked(null);
}
}
// ------------------------------------------------ //
// --
// ------------------------------------------------ //
@Override
protected void initEventHandlers() {
}
}