com.bekwam.mavenpomupdater.MainViewController.java Source code

Java tutorial

Introduction

Here is the source code for com.bekwam.mavenpomupdater.MainViewController.java

Source

/*
 * Copyright 2014 Bekwam, Inc 
 * 
 * Licensed 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 com.bekwam.mavenpomupdater;

import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.ComboBox;
import javafx.scene.control.IndexRange;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.DirectoryChooser;
import javafx.stage.Window;

import javax.inject.Inject;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.bekwam.mavenpomupdater.data.FavoritesDAO;

/**
 * Controller to support the main screen of MPU
 * 
 * @author carlwalker
 * @since 1.0.0
 */
public class MainViewController {

    private Log log = LogFactory.getLog(MainViewController.class);

    @FXML
    VBox vbox;

    @FXML
    GridPane gp; // for hi-dpi treatment

    @FXML
    ComboBox<String> cbRootDir;

    @FXML
    TextField tfFilters;

    @FXML
    TextField tfNewVersion;

    @FXML
    TableView<POMObject> tblPOMS;

    @FXML
    TableColumn<POMObject, String> tcPath;

    @FXML
    TableColumn<POMObject, String> tcVersion;

    @FXML
    TableColumn<POMObject, String> tcParentVersion;

    @FXML
    TableColumn<POMObject, Boolean> tcUpdate;

    @FXML
    ImageView aboutImageView;

    @FXML
    TabPane tabPane;

    @FXML
    Tab homeTab;

    @FXML
    Tab aboutTab;

    @FXML
    Tab errorLogTab;

    @FXML
    Label aboutVersionLabel;

    @FXML
    MenuItem miCut;

    @FXML
    MenuItem miCopy;

    @FXML
    MenuItem miPaste;

    @FXML
    CheckMenuItem miErrorLog;

    @FXML
    Button tbCut;

    @FXML
    Button tbCopy;

    @FXML
    Button tbPaste;

    @FXML
    Button tbScan;

    @FXML
    Button tbUpdate;

    @FXML
    Button tbClear;

    @FXML
    TableView<ErrorLogEntry> tblErrors;

    @FXML
    TableColumn<ErrorLogEntry, String> tcTime;

    @FXML
    TableColumn<ErrorLogEntry, String> tcFile;

    @FXML
    TableColumn<ErrorLogEntry, String> tcMessage;

    @FXML
    AnchorPane ap;

    @FXML
    Button btnSelectAll;

    @FXML
    Button btnDeselectAll;

    @FXML
    Button btnLockUnlock;

    @Inject
    MenuBarDelegate menuBarDelegate;

    @Inject
    AboutDelegate aboutDelegate;

    @Inject
    ToolBarDelegate toolBarDelegate;

    @Inject
    ErrorLogDelegate errorLogDelegate;

    //
    // will be manually wired
    //
    AlertController alertController;

    @Inject
    FavoritesDAO favoritesDAO;

    @Inject
    PropertiesFileDAO propertiesFileDAO;

    private DocumentBuilderFactory factory;
    private TextField tfRootDir;
    private Boolean tblPOMSLocked = true;
    private Boolean tblPOMSDirty = false;
    private ImageView lockImageView;
    private ImageView unlockImageView;

    public MainViewController() {

        if (log.isDebugEnabled()) {
            log.debug("[CONTROLLER]");
        }

        factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
    }

    @FXML
    public void initialize() {

        if (log.isDebugEnabled()) {
            log.debug("[INIT]");
        }

        vbox.getStyleClass().add("main-view-pane");

        initialTblPOMS();

        Properties appProperties = propertiesFileDAO.getProperties();
        String version = appProperties.getProperty(AppPropertiesKeys.VERSION);

        initializeButtonGraphics();

        errorLogTab.setOnSelectionChanged(event -> tbClear.setDisable(!errorLogTab.isSelected()));

        //
        // tfRootDir comes from a ComboBox that isn't readily accessible
        //

        tfRootDir = cbRootDir.editorProperty().get();
        tfRootDir.setOnMouseReleased(event -> {
            toolBarMouseReleased(event);
        });

        //
        // wire up delegates
        //
        aboutDelegate.imageView = aboutImageView;
        aboutDelegate.tabPane = tabPane;
        aboutDelegate.aboutTab = aboutTab;
        aboutDelegate.version = version;
        aboutDelegate.aboutVersionLabel = aboutVersionLabel;

        menuBarDelegate.tabPane = tabPane;
        menuBarDelegate.homeTab = homeTab;
        menuBarDelegate.aboutTab = aboutTab;
        menuBarDelegate.errorLogTab = errorLogTab;
        menuBarDelegate.supportURL = appProperties.getProperty(AppPropertiesKeys.SUPPORT_URL);
        menuBarDelegate.licenseURL = appProperties.getProperty(AppPropertiesKeys.LICENSE_URL);
        menuBarDelegate.miCut = miCut;
        menuBarDelegate.miCopy = miCopy;
        menuBarDelegate.miPaste = miPaste;
        menuBarDelegate.tfFilters = tfFilters;
        menuBarDelegate.tfNewVersion = tfNewVersion;
        menuBarDelegate.tfRootDir = tfRootDir;

        toolBarDelegate.tbCut = tbCut;
        toolBarDelegate.tbCopy = tbCopy;
        toolBarDelegate.tbPaste = tbPaste;
        toolBarDelegate.tfFilters = tfFilters;
        toolBarDelegate.tfNewVersion = tfNewVersion;
        toolBarDelegate.tfRootDir = tfRootDir;

        errorLogDelegate.tabPane = tabPane;
        errorLogDelegate.errorLogTab = errorLogTab;
        errorLogDelegate.tblErrors = tblErrors;
        errorLogDelegate.miErrorLog = miErrorLog;

        //
        // initialize delegates
        //
        aboutDelegate.init();
        toolBarDelegate.init();
        errorLogDelegate.init();

        favoritesDAO.init();

        List<String> favorites = favoritesDAO.findAllFavoriteRootDirs();

        cbRootDir.getItems().addAll(favorites);
    }

    private void initialTblPOMS() {
        tcPath.setCellValueFactory(new PropertyValueFactory<POMObject, String>("absPath"));
        tcPath.setCellFactory(new WarningCellFactory());

        tcVersion.setCellValueFactory(new PropertyValueFactory<POMObject, String>("version"));
        tcVersion.setCellFactory(new WarningCellFactory());
        tcVersion.setOnEditCommit(t -> {
            tblPOMSDirty = true;
            ((POMObject) t.getTableView().getItems().get(t.getTablePosition().getRow()))
                    .setVersion(t.getNewValue());
        });

        tcParentVersion.setCellValueFactory(new PropertyValueFactory<POMObject, String>("parentVersion"));
        tcParentVersion.setCellFactory(new WarningCellFactory());
        tcParentVersion.setOnEditCommit(t -> {
            tblPOMSDirty = true;
            ((POMObject) t.getTableView().getItems().get(t.getTablePosition().getRow()))
                    .setParentVersion(t.getNewValue());
        });

        tcUpdate.setCellValueFactory(new PropertyValueFactory<POMObject, Boolean>("update"));
        tcUpdate.setCellFactory(CheckBoxTableCell.forTableColumn(tcUpdate));

        tcTime.setCellValueFactory(new PropertyValueFactory<ErrorLogEntry, String>("logTime"));

        tcFile.setCellValueFactory(new PropertyValueFactory<ErrorLogEntry, String>("fileName"));

        tcMessage.setCellValueFactory(new PropertyValueFactory<ErrorLogEntry, String>("message"));
    }

    private void initializeButtonGraphics() {
        Image cutImage = new Image("images/cut32.png");
        tbCut.setGraphic(new ImageView(cutImage));

        Image copyImage = new Image("images/copy32.png");
        tbCopy.setGraphic(new ImageView(copyImage));

        Image pasteImage = new Image("images/paste32.png");
        tbPaste.setGraphic(new ImageView(pasteImage));

        Image scanImage = new Image("images/scan32.png");
        tbScan.setGraphic(new ImageView(scanImage));

        Image updateImage = new Image("images/update32.png");
        tbUpdate.setGraphic(new ImageView(updateImage));

        Image clearImage = new Image("images/clear32.png");
        tbClear.setGraphic(new ImageView(clearImage));

        Image selImage = new Image("images/select16.png");
        btnSelectAll.setGraphic(new ImageView(selImage));

        Image deselImage = new Image("images/deselect16.png");
        btnDeselectAll.setGraphic(new ImageView(deselImage));

        Image lockImage = new Image("images/lock16.png");
        lockImageView = new ImageView(lockImage);

        Image unlockImage = new Image("images/unlock16.png");
        unlockImageView = new ImageView(unlockImage);
        btnLockUnlock.setGraphic(unlockImageView);
    }

    @FXML
    public void selectFile(ActionEvent evt) {

        if (log.isDebugEnabled()) {
            log.debug("[SELECT FILE]");
        }

        Button b = (Button) evt.getSource();

        Window w = b.getParent().getScene().getWindow();

        DirectoryChooser dirChooser = new DirectoryChooser();

        File dir = dirChooser.showDialog(w);
        if (dir != null) {
            tfRootDir.setText(dir.getAbsolutePath());
        }
    }

    @FXML
    public void scan() {

        if (log.isDebugEnabled()) {
            log.debug("[SCAN]");
        }

        String rootDir = tfRootDir.getText();

        if (StringUtils.isEmpty(rootDir)) {

            if (log.isDebugEnabled()) {
                log.debug("[SCAN] rootDir is empty");
            }

            alertController.setNotificationDialog("Project Root Is Empty",
                    "A root directory must be specified in order to scan.");
            vbox.toBack(); // bring up the alert view

            tblPOMS.getItems().clear();
            btnDeselectAll.setDisable(true);
            btnSelectAll.setDisable(true);

            lockTblPOMS();
            btnLockUnlock.setDisable(true);

            tfNewVersion.setDisable(false);

            return;
        }

        //
        // record the rootDir as a favorite (if needed)
        //
        favoritesDAO.addFavoriteRootDir(rootDir);

        if (!cbRootDir.getItems().contains(rootDir)) {
            cbRootDir.getItems().add(rootDir);
        }

        String filtersCSV = tfFilters.getText();

        CSVFilenameFilter ff = new CSVFilenameFilter(filtersCSV);

        List<String> pomPaths = new ArrayList<String>();
        gatherPOMPaths(rootDir, pomPaths, ff);

        if (CollectionUtils.isEmpty(pomPaths)) {

            if (log.isDebugEnabled()) {
                log.debug("[SCAN] pomPaths is empty");
            }

            alertController.setNotificationDialog("No POMs Found",
                    "No pom.xml files were found in the specified Project Root.");
            vbox.toBack(); // bring up the alert view

            tblPOMS.getItems().clear();
            btnDeselectAll.setDisable(true);
            btnSelectAll.setDisable(true);

            lockTblPOMS();
            btnLockUnlock.setDisable(true);

            tfNewVersion.setDisable(false);

            return;
        }

        tblPOMS.getItems().clear();
        for (String path : pomPaths) {
            POMObject pomObject = parseFile(path);
            tblPOMS.getItems().add(pomObject);
        }

        //
        // Activate the select and de-select buttons
        //
        btnDeselectAll.setDisable(false);
        btnSelectAll.setDisable(false);

        //
        // Activate the lock/unlock button
        //
        lockTblPOMS(); // reset lock state
        btnLockUnlock.setDisable(false);
        tblPOMSDirty = false;
        tfNewVersion.setDisable(false);
    }

    private POMObject parseFile(String path) {

        if (log.isDebugEnabled()) {
            log.debug("[PARSE] path=" + path);
        }

        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(path);

            XPath xpath = XPathFactory.newInstance().newXPath();
            XPathExpression expression = xpath.compile("//project/version/text()");
            Node node = (Node) expression.evaluate(doc, XPathConstants.NODE);

            String version = "";
            if (node != null) {
                version = node.getNodeValue();
                if (log.isDebugEnabled()) {
                    log.debug("[PARSE]    version=" + node.getNodeValue());
                }
            }

            //           XPath pvXPath = XPathFactory.newInstance().newXPath();
            XPathExpression pvExpression = xpath.compile("//project/parent/version/text()");
            Node pvNode = (Node) pvExpression.evaluate(doc, XPathConstants.NODE);

            String pVersion = "";
            if (pvNode != null) {
                pVersion = pvNode.getNodeValue();
                if (log.isDebugEnabled()) {
                    log.debug("[PARSE]    parentVersion=" + pvNode.getNodeValue());
                }
            }

            return new POMObject(true, path, version, pVersion, false);

        } catch (Exception exc) {
            log.error("error parsing path=" + path, exc);

            errorLogDelegate.log(path, exc.getMessage());

            return new POMObject(false, path, "Parse Error (will be skipped)", "Parse Error (will be skipped)",
                    true);
        }
    }

    private void gatherPOMPaths(String rootDir, List<String> pomPaths, FilenameFilter ff) {

        if (StringUtils.isEmpty(rootDir)) {

            if (log.isDebugEnabled()) {
                log.debug("[GATHER] rootDir is empty");
            }

            return;
        }

        File rd = new File(rootDir);

        if (!rd.exists()) {
            if (log.isDebugEnabled()) {
                log.debug("[SCAN] rootDir does not exist");
            }
            alertController.setNotificationDialog("Project Root Does Not Exist",
                    "The specified root directory '" + rootDir + "' does not exist.");
            vbox.toBack(); // bring up the alert view
            return;
        }

        if (rd.isFile()) {

            if (rd.getName().equals("pom.xml")) {
                pomPaths.add(rd.getAbsolutePath());
            }

        } else {
            for (File f : rd.listFiles(ff)) {
                gatherPOMPaths(f.getAbsolutePath(), pomPaths, ff);
            }
        }
    }

    @FXML
    public void update() {

        if (log.isDebugEnabled()) {
            log.debug("[UPDATE]");
        }

        String newVersion = tfNewVersion.getText();
        if (StringUtils.isEmpty(newVersion) && !tblPOMSDirty) {
            if (log.isDebugEnabled()) {
                log.debug("[UPDATE] newVersion is empty and not manually editing the table");
            }
            alertController.setNotificationDialog("No Version Specified", "Please specify a version.");
            vbox.toBack(); // bring up the alert view
            return;
        }

        long npoms = tblPOMS.getItems().stream().filter(p -> p.getUpdate()).count();
        if (npoms == 0L) {
            if (log.isDebugEnabled()) {
                log.debug("[UPDATE] tblPOMS is empty");
            }
            alertController.setNotificationDialog("No POMs Specified",
                    "No poms were specified.\nBrowse for a Project Root, press Scan, and select one or more poms.");
            vbox.toBack(); // bring up the alert view
            return;
        } else {

            if (log.isDebugEnabled()) {
                log.debug("[UPDATE] confirming update operation");
            }
            alertController.setConfirmationDialog("Confirm Update",
                    "This wil update " + npoms + " POM files.  Continue?", mv -> mv.doUpdate());

            vbox.toBack();
        }
    }

    public void doUpdate() {

        for (POMObject p : tblPOMS.getItems()) {

            if (log.isDebugEnabled()) {
                log.debug("[DO UPDATE] p=" + p.getAbsPath());
            }

            if (p.getParseError()) {
                if (log.isDebugEnabled()) {
                    log.debug("[DO UPDATE] skipping update of p=" + p.getAbsPath()
                            + " because of a parse error on scanning");
                }
                continue;
            }

            if (!p.getUpdate()) {
                if (log.isDebugEnabled()) {
                    log.debug("[DO UPDATE] skipping update of p=" + p.getAbsPath()
                            + " because user excluded it from update");
                }
                continue;
            }

            try {
                DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
                factory.setNamespaceAware(false);
                DocumentBuilder builder = factory.newDocumentBuilder();
                Document doc = builder.parse(p.getAbsPath());

                if (p.getParentVersion() != null && p.getParentVersion().length() > 0) {
                    XPath xpath = XPathFactory.newInstance().newXPath();
                    XPathExpression expression = xpath.compile("//project/parent/version/text()");
                    Node node = (Node) expression.evaluate(doc, XPathConstants.NODE);

                    if (StringUtils.isNotEmpty(tfNewVersion.getText())) {
                        node.setNodeValue(tfNewVersion.getText());
                    } else { // editing individual table cells
                        node.setNodeValue(p.getParentVersion());
                    }
                }

                if (p.getVersion() != null && p.getVersion().length() > 0) {
                    XPath xpath = XPathFactory.newInstance().newXPath();
                    XPathExpression expression = xpath.compile("//project/version/text()");
                    Node node = (Node) expression.evaluate(doc, XPathConstants.NODE);

                    if (StringUtils.isNotEmpty(tfNewVersion.getText())) {
                        node.setNodeValue(tfNewVersion.getText());
                    } else { // editing individual table cells
                        node.setNodeValue(p.getVersion());
                    }
                }

                TransformerFactory tFactory = TransformerFactory.newInstance();
                Transformer transformer = tFactory.newTransformer();

                String workingFileName = p.getAbsPath() + ".mpu";
                FileWriter fw = new FileWriter(workingFileName);
                DOMSource source = new DOMSource(doc);
                StreamResult result = new StreamResult(fw);
                transformer.transform(source, result);
                fw.close();

                Path src = FileSystems.getDefault().getPath(workingFileName);
                Path target = FileSystems.getDefault().getPath(p.getAbsPath());

                Files.copy(src, target, StandardCopyOption.REPLACE_EXISTING);

                Files.delete(src);

            } catch (Exception exc) {
                log.error("error updating poms", exc);
            }
        }

        if (StringUtils.isNotEmpty(tfRootDir.getText())) {
            if (log.isDebugEnabled()) {
                log.debug("[DO UPDATE] issuing rescan command");
            }
            scan();
        } else {
            if (log.isDebugEnabled()) {
                log.debug("[DO UPDATE] did an update, but there is not value in root; clearing");
            }
            tblPOMS.getItems().clear();
        }
        tblPOMSDirty = false;
        tfNewVersion.setDisable(false);
    }

    @FXML
    public void close() {
        menuBarDelegate.close();
    }

    @FXML
    public void showAbout() {
        menuBarDelegate.showAbout();
    }

    @FXML
    public void showOrHideErrorLog(ActionEvent evt) {
        CheckMenuItem mi = (CheckMenuItem) evt.getSource();
        if (mi.isSelected()) {
            menuBarDelegate.showErrorLog();
        } else {
            menuBarDelegate.hideErrorLog();
        }
    }

    @FXML
    public void browseSupport() {
        menuBarDelegate.browseSupport();
    }

    @FXML
    public void browseLicense() {
        menuBarDelegate.browseLicense();
    }

    @FXML
    public void cut() {
        menuBarDelegate.cut();
    }

    @FXML
    public void copy() {
        menuBarDelegate.copy();
    }

    @FXML
    public void paste() {
        menuBarDelegate.paste();
    }

    @FXML
    public void tbCut() {
        toolBarDelegate.cut();
    }

    @FXML
    public void tbCopy() {
        toolBarDelegate.copy();
    }

    @FXML
    public void tbPaste() {
        toolBarDelegate.paste();
    }

    @FXML
    public void showingEditMenu() {
        menuBarDelegate.showingEditMenu();
    }

    @FXML
    public void toolBarMouseReleased(MouseEvent evt) {

        TextField tf = (TextField) evt.getSource();
        String selectedText = tf.getSelectedText();
        IndexRange selectedRange = tf.getSelection();

        toolBarDelegate.updateToolBarForClipboard(tf, selectedText, selectedRange);
    }

    public void adjustForHiDPI() {
        if (log.isDebugEnabled()) {
            log.debug("[ADJUST]");
        }
        gp.setPrefHeight(270.0);
    }

    @FXML
    public void mouseTest(MouseEvent evt) {
        log.debug("[MOUSE] evt type=" + evt.getEventType());

    }

    @FXML
    public void closingErrorLogTab() {
        errorLogDelegate.closingTab();
    }

    @FXML
    public void clearErrorLog() {
        errorLogDelegate.clearTable();
    }

    @FXML
    public void selectAllTblPOMS() {

        if (log.isDebugEnabled()) {
            log.debug("[SELECT ALL]");
        }
        tblPOMS.getItems().stream().filter(pom -> !pom.getParseError()).forEach(pom -> pom.setUpdate(true));
    }

    @FXML
    public void deSelectAllTblPOMS() {

        if (log.isDebugEnabled()) {
            log.debug("[DESELECT ALL]");
        }

        tblPOMS.getItems().stream().forEach(pom -> pom.setUpdate(false));
    }

    @FXML
    public void handleLockTblPOMS() {

        if (log.isDebugEnabled()) {
            log.debug("[HANDLE LOCK]");
        }

        if (tblPOMSLocked) { // unlock operation
            unlockTblPOMS();
        } else { // lock operation
            lockTblPOMS();
        }
    }

    private void unlockTblPOMS() {
        tblPOMSLocked = false;

        tcVersion.setEditable(true);
        tcParentVersion.setEditable(true);

        btnLockUnlock.setGraphic(lockImageView);

        //
        // operating in manual mode which ignores the global New Version
        //
        tfNewVersion.setText(null);
        tfNewVersion.setDisable(true);
    }

    private void lockTblPOMS() {
        tblPOMSLocked = true;

        tcVersion.setEditable(false);
        tcParentVersion.setEditable(false);

        btnLockUnlock.setGraphic(unlockImageView);
    }
}