Java tutorial
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package utilitybasedfx; import bikeshed.*; import java.io.File; import java.io.IOException; import java.net.URL; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.Calendar; import java.util.Optional; import java.util.ResourceBundle; import javafx.application.Platform; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.concurrent.Task; import javafx.concurrent.Worker; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.Alert; import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.CheckBoxTreeItem; import javafx.scene.control.ChoiceDialog; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.TextInputDialog; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.control.cell.CheckBoxTreeCell; import javafx.scene.paint.Paint; import org.apache.commons.io.FileUtils; /** * FXML Controller class * * @author David */ public class MainGUIController implements Initializable { @FXML private MenuItem mnuFileNew; @FXML private MenuItem mnuFileOpen; @FXML private Label lblProjectName; @FXML private Label lblSourceStatus; @FXML private Label lblCompiledStatus; @FXML private Button btnImportSource; @FXML private Button btnImportCompiled; @FXML private Button btnCompileFromSource; @FXML private Button btnRecheckProjectFolders; @FXML private TreeView treeFilesToMutate; @FXML private TreeView treeTraditionalOps; @FXML private TreeView treeClassOps; @FXML private Button btnGenerate; @FXML private Label lblWorking; @FXML private ProgressIndicator barWorking; @FXML private Label lblWorkingTask; ArrayList<File> listOfSelectedFiles = new ArrayList<File>(); ArrayList<String> listOfTraditionalOps = new ArrayList<String>(); ArrayList<String> listOfClassOps = new ArrayList<String>(); boolean shouldRefreshFileTree; /** * Initializes the controller class. */ @Override public void initialize(URL url, ResourceBundle rb) { disableWorking(); Prefs.setProjectLoaded(false); setupOpsTrees(); shouldRefreshFileTree = false; //checkProjectLoaded(); } @FXML public void eventFileNew(ActionEvent event) { SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd_HHmmss"); TextInputDialog dialog = new TextInputDialog("Project_" + sdf.format((Calendar.getInstance().getTime()))); dialog.setTitle("Please specify a project name"); dialog.setHeaderText("This will be the name of your project and cannot easily be changed.\n" + "The name should be a legal filename."); dialog.setContentText("New project name:"); boolean shouldContinue = true; boolean resultValid = false; String res = ""; do { Optional<String> result = dialog.showAndWait(); if (result.isPresent()) { res = result.get(); if (Utils.validFileName(res)) { if (Utils.dirExist(Prefs.getSavePath() + "/" + res)) { dialog.setHeaderText("This will be the name of your project and cannot easily be changed.\n" + "The name should be a legal filename.\n\n" + "This project already exists"); } else { resultValid = true; shouldContinue = false; } } else { dialog.setHeaderText("This will be the name of your project and cannot easily be changed.\n" + "The name should be a legal filename.\n\n" + "The name you specified is not legal, try again"); } } else { shouldContinue = false; } } while (shouldContinue); if (resultValid) { createProject(res); setActiveProject(res); } } @FXML public void eventFileOpen(ActionEvent event) { boolean success = false; boolean projectsFound = false; File files[] = new File(Prefs.getSavePath()).listFiles(); if (files != null) { ArrayList<String> choices = new ArrayList<String>(); for (File f : files) { if (f != null && f.isDirectory()) { choices.add(f.getName()); } } if (!choices.isEmpty()) { ChoiceDialog<String> dialog = new ChoiceDialog<String>(choices.get(0), choices); dialog.setTitle("Open Project"); dialog.setHeaderText("Please choose a project you would like to load"); dialog.setContentText("Project:"); Optional<String> result = dialog.showAndWait(); if (result.isPresent()) { success = true; setActiveProject(result.get()); } projectsFound = true; } } if (!success) { shouldRefreshFileTree = true; } if (!projectsFound) { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("No projects found"); alert.setHeaderText("There are no projects found in the following folder"); alert.setContentText(new File(Prefs.getSavePath()).getAbsolutePath()); alert.showAndWait(); } } @FXML private void eventFileClose(ActionEvent event) { Platform.exit(); System.exit(0); } @FXML public void eventImportSource(ActionEvent event) { File selectedDir = Utils.dirChooser("Please choose the project source root folder"); File sourceDir = new File(Prefs.getSourcePath()); boolean shouldContinue = true; if (selectedDir != null) { if (sourceDir.isDirectory()) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("This project already exists!"); alert.setHeaderText("This project already has a src folder meaning it already has files imported"); alert.setContentText( "Are you sure you want to import the new source?\nPressing OK will delete the files previous imported to this project and replace them with the new files."); Optional<ButtonType> result = alert.showAndWait(); if (result.get() == ButtonType.OK) { sourceDir.delete(); } else { shouldContinue = false; } } if (shouldContinue) { shouldRefreshFileTree = true; enableWorking(new Task<String>() { @Override protected String call() throws InterruptedException { updateMessage("Copying Files..."); try { FileUtils.copyDirectory(selectedDir, sourceDir); } catch (IOException e) { Utils.stackErrorDialog(e); } updateMessage(""); return ""; //[FIXME] find a way to do this inline function without a class as return type } }); } } } @FXML private void eventImportCompiled(ActionEvent event) { File selectedDir = Utils.dirChooser("Please choose the project classes root folder"); File classDir = new File(Prefs.getClassPath()); boolean shouldContinue = true; if (selectedDir != null) { if (classDir.isDirectory()) { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("This project already exists!"); alert.setHeaderText( "This project already has a class folder meaning it already has files imported"); alert.setContentText( "Are you sure you want to import the new source?\nPressing OK will delete the files previous imported to this project and replace them with the new files."); Optional<ButtonType> result = alert.showAndWait(); if (result.get() == ButtonType.OK) { classDir.delete(); } else { shouldContinue = false; } } if (shouldContinue) { shouldRefreshFileTree = true; enableWorking(new Task<String>() { @Override protected String call() throws InterruptedException { updateMessage("Copying Files..."); try { FileUtils.copyDirectory(selectedDir, classDir); } catch (IOException e) { Utils.stackErrorDialog(e); } updateMessage(""); return ""; //[FIXME] find a way to do this inline function without a class as return type } }); } } } @FXML private void eventCompileFromSource(ActionEvent event) { // [TODO] use javac to compile from source Alert alert = new Alert(AlertType.ERROR); alert.setTitle("This is not yet implemented"); alert.setHeaderText("in the future pressing this will generate the .class files from the source"); alert.setContentText("but as of now it is not implemented"); alert.showAndWait(); shouldRefreshFileTree = true; } @FXML private void eventRecheckProjectFolders(ActionEvent event) { shouldRefreshFileTree = true; checkProjectLoaded(); } @FXML private void eventGenerateMutants(ActionEvent event) { MutantViewerController c = Prefs.getController("MutantViewer"); List<File> actualFiles = new ArrayList<>(); for (File file : listOfSelectedFiles) { actualFiles.add(new File(Prefs.getSourcePath(), file.toString())); } enableWorking(new Task<String>() { @Override protected String call() throws InterruptedException { File root; try { root = new File(Prefs.getProjectPath()).getCanonicalFile(); } catch (IOException ioe) { updateMessage("Unable to find root directory"); return ""; } NewMutationSystem nms = new NewMutationSystem(root); updateMessage("Generating Mutants!..."); try { List<Mutant> mutants = nms.generateMutants(actualFiles, listOfTraditionalOps, listOfClassOps); updateMessage(""); c.setMutants(mutants); Platform.runLater(c::refreshClassesCbos); Platform.runLater(() -> { Prefs.getStage().setScene(Prefs.getScene("MutantViewer")); }); } catch (UnableToGenerateException utg) { Platform.runLater(() -> { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("There was an error"); alert.setHeaderText("There was an issue generating mutants"); alert.setContentText(utg.toString()); alert.showAndWait(); }); } catch (NoClassFilesException ex) { Platform.runLater(() -> { Alert alert = new Alert(AlertType.ERROR); alert.setTitle("There was an error"); alert.setHeaderText("There was an issue generating mutants"); alert.setContentText( "There is no classes in the following folder\n" + Prefs.getClassPath()); alert.showAndWait(); }); } return ""; //[FIXME] find a way to do this inline function without a class as return type } }); } private void createProject(String res) { Prefs.setProjectName(res); File f = new File(Prefs.getProjectPath()); try { f.mkdirs(); } catch (Exception e) { Utils.stackErrorDialog(e); } } private void setActiveProject(String name) { Prefs.setProjectName(name); lblProjectName.setText(name); Prefs.setProjectLoaded(true); shouldRefreshFileTree = true; checkProjectLoaded(); } public boolean checkProjectLoaded() { boolean fullyLoaded = true; if (Prefs.getProjectLoaded()) { btnImportSource.setDisable(false); btnImportCompiled.setDisable(false); btnRecheckProjectFolders.setDisable(false); if (Prefs.getSourceImported()) { lblSourceStatus.setText("Found"); lblSourceStatus.setTextFill(Paint.valueOf("GREEN")); btnCompileFromSource.setDisable(false); } else { lblSourceStatus.setText("Not Found"); lblSourceStatus.setTextFill(Paint.valueOf("RED")); btnCompileFromSource.setDisable(true); fullyLoaded = false; } if (Prefs.getClassImported()) { lblCompiledStatus.setText("Found"); lblCompiledStatus.setTextFill(Paint.valueOf("GREEN")); } else { lblCompiledStatus.setText("Not Found"); lblCompiledStatus.setTextFill(Paint.valueOf("RED")); fullyLoaded = false; } } else { btnImportSource.setDisable(true); btnImportCompiled.setDisable(true); btnCompileFromSource.setDisable(true); btnRecheckProjectFolders.setDisable(true); fullyLoaded = false; } if (fullyLoaded) { if (shouldRefreshFileTree) { refreshFilesTree(); } } else { clearFilesTree(); } checkIfGenerateShouldBeEnabled(); return fullyLoaded; } private void refreshFilesTree() { shouldRefreshFileTree = false; CheckBoxTreeItem<String> root; root = new CheckBoxTreeItem("root"); root.setExpanded(true); addChildren(root, new File(Prefs.getSourcePath())); treeFilesToMutate.setStyle("-fx-font-size: 11; "); //treeFilesToMutate.getStylesheets().add("/css/treeViewColouredSelection.css"); // [FIXME] make the color change! WHY IT NO WORK treeFilesToMutate.setRoot(root); treeFilesToMutate.setShowRoot(false); treeFilesToMutate.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); } // Returns false if folder and branches have no java files private boolean addChildren(TreeItem parentBranch, File parentFile) { boolean containsJavaFiles = false; File children[] = parentFile.listFiles(); if (children != null) { ArrayList<File> directories = new ArrayList<File>(); ArrayList<File> files = new ArrayList<File>(); for (File child : children) { if (child.isDirectory()) { directories.add(child); } else { files.add(child); } } for (File dir : directories) { CheckBoxTreeItem<String> treeItem = new CheckBoxTreeItem<>(dir.getName()); treeItem.setExpanded(true); if (addChildren(treeItem, dir)) { containsJavaFiles = true; parentBranch.getChildren().add(treeItem); } } for (File file : files) { if (file.getName().endsWith(".java")) { CheckBoxTreeItem<String> treeItem = new CheckBoxTreeItem<>(file.getName()); treeItem.selectedProperty().addListener(new ChangeListener<Boolean>() { public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (newValue) { addFilepathToArraylist(treeItem); } else { removeFilepathFromArraylist(treeItem); } } }); parentBranch.getChildren().add(treeItem); containsJavaFiles = true; } } } return containsJavaFiles; } private void clearFilesTree() { shouldRefreshFileTree = false; CheckBoxTreeItem<String> root; root = new CheckBoxTreeItem("root"); root.setExpanded(true); treeFilesToMutate.setRoot(root); treeFilesToMutate.setShowRoot(false); } private void addFilepathToArraylist(CheckBoxTreeItem<String> treeItem) { String path = treeItem.valueProperty().get(); CheckBoxTreeItem<String> parent = (CheckBoxTreeItem) treeItem.getParent(); while (parent != null && !parent.valueProperty().get().equals("root")) { treeItem = parent; parent = (CheckBoxTreeItem) treeItem.getParent(); path = treeItem.valueProperty().get() + "/" + path; } listOfSelectedFiles.add(new File(path)); checkIfGenerateShouldBeEnabled(); } private void removeFilepathFromArraylist(CheckBoxTreeItem<String> treeItem) { String path = treeItem.valueProperty().get(); CheckBoxTreeItem<String> parent = (CheckBoxTreeItem) treeItem.getParent(); while (parent != null && !parent.valueProperty().get().equals("root")) { treeItem = parent; parent = (CheckBoxTreeItem) treeItem.getParent(); path = treeItem.valueProperty().get() + "/" + path; } listOfSelectedFiles.remove(new File(path)); checkIfGenerateShouldBeEnabled(); } private void setupOpsTrees() { CheckBoxTreeItem<String> traditionalRoot = preSetupOpsTree(treeTraditionalOps); CheckBoxTreeItem<String> classRoot = preSetupOpsTree(treeClassOps); String traditionalOps[] = { "-Arithmetic", "AOR", "AOI", "AOD", "-Relational", "ROR", "-Conditional", "COR", "COI", "COD", "-Shift", "SOR", "-Logical", "LOR", "LOI", "LOD", "-Assignment", "ASR", "-Deletion", "SDL", "VDL", "CDL", "ODL" }; String classOps[] = { "-Encapsulation", "AMC", "-Inheritance", "IHD", "IHI", "IOD", "IOP", "IOR", "ISI", "ISD", "IPC", "-Polymorphism", "PNC", "PMD", "PPD", "PCI", "PCC", "PCD", "PRV", "OMR", "OMD", "OAC", "-Java-Specific Features", "JTI", "JTD", "JSI", "JSD", "JID", "JDC", "EOA", "EOC", "EAM", "EMM" }; fillTreeFromArray(traditionalOps, traditionalRoot, listOfTraditionalOps); fillTreeFromArray(classOps, classRoot, listOfClassOps); } private void fillTreeFromArray(String[] ops, CheckBoxTreeItem<String> root, ArrayList<String> listForOpsEvent) { CheckBoxTreeItem<String> subFolder = null; for (String op : ops) { if (op.startsWith("-")) { subFolder = new CheckBoxTreeItem<String>(op.substring(1)); subFolder.setExpanded(true); root.getChildren().add(subFolder); } else { CheckBoxTreeItem<String> opTreeItem = new CheckBoxTreeItem<String>(op); if (subFolder != null) { subFolder.getChildren().add(opTreeItem); opTreeItem.selectedProperty().addListener(new ChangeListener<Boolean>() { public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if (newValue) { addOpToArraylist(opTreeItem, listForOpsEvent); } else { removeOpFromArraylist(opTreeItem, listForOpsEvent); } } }); } } } } private CheckBoxTreeItem<String> preSetupOpsTree(TreeView tree) { CheckBoxTreeItem<String> root = new CheckBoxTreeItem("root"); root.setExpanded(true); tree.setStyle("-fx-font-size: 11; "); //tree.getStylesheets().add("/css/treeViewColouredSelection.css"); // [FIXME] make the color change! WHY IT NO WORK tree.setRoot(root); tree.setShowRoot(false); tree.setCellFactory(CheckBoxTreeCell.<String>forTreeView()); return root; } private void addOpToArraylist(CheckBoxTreeItem<String> treeItem, ArrayList<String> list) { String opName = treeItem.valueProperty().get(); list.add(opName); checkIfGenerateShouldBeEnabled(); } private void removeOpFromArraylist(CheckBoxTreeItem<String> treeItem, ArrayList<String> list) { String opName = treeItem.valueProperty().get(); list.remove(opName); checkIfGenerateShouldBeEnabled(); } private void checkIfGenerateShouldBeEnabled() { if (listOfSelectedFiles.isEmpty() || (listOfTraditionalOps.isEmpty() && listOfClassOps.isEmpty())) { btnGenerate.setDisable(true); } else { btnGenerate.setDisable(false); } } private void enableWorking(Task task) { btnImportSource.setDisable(true); btnImportCompiled.setDisable(true); btnCompileFromSource.setDisable(true); btnGenerate.setDisable(true); lblWorking.setVisible(true); barWorking.setVisible(true); lblWorkingTask.setVisible(true); lblWorkingTask.textProperty().bind(task.messageProperty()); barWorking.progressProperty().bind(task.progressProperty()); task.stateProperty().addListener(new ChangeListener<Worker.State>() { @Override public void changed(ObservableValue<? extends Worker.State> observableValue, Worker.State oldState, Worker.State newState) { if (newState == Worker.State.SUCCEEDED) { disableWorking(); } else if (newState == Worker.State.FAILED) { disableWorking(); //Utils.stackErrorDialog((Exception)task.getException()); task.getException().printStackTrace(); Alert alert = new Alert(AlertType.ERROR); alert.setTitle("There was an error"); alert.setHeaderText("There was some kind of error while trying to do the current task"); alert.setContentText(task.getException().toString()); alert.showAndWait(); } } }); new Thread(task).start(); } private void disableWorking() { lblWorking.setVisible(false); barWorking.setVisible(false); lblWorkingTask.setVisible(false); checkProjectLoaded(); } }