be.makercafe.apps.makerbench.editors.XMLEditor.java Source code

Java tutorial

Introduction

Here is the source code for be.makercafe.apps.makerbench.editors.XMLEditor.java

Source

/*
Copyright 2015 - 2016, Luc De pauw - Makercafe.be
This file is part of Makerbench.
    
Makerbench is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Makerbench is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with Makerbench.  If not, see <http://www.gnu.org/licenses/>.
*/
package be.makercafe.apps.makerbench.editors;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.LineNumberFactory;
import org.fxmisc.richtext.StyleSpans;
import org.fxmisc.richtext.StyleSpansBuilder;

import de.jensd.fx.glyphs.GlyphsDude;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
import javafx.event.ActionEvent;
import javafx.geometry.Orientation;
import javafx.scene.Group;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;

public class XMLEditor extends Editor {
    private static final Pattern XML_TAG = Pattern
            .compile("(?<ELEMENT>(</?\\h*)(\\w+)([^<>]*)(\\h*/?>))" + "|(?<COMMENT><!--[^<>]+-->)");
    private static final Pattern ATTRIBUTES = Pattern.compile("(\\w+\\h*)(=)(\\h*\"[^\"]+\")");
    private static final int GROUP_OPEN_BRACKET = 2;
    private static final int GROUP_ELEMENT_NAME = 3;
    private static final int GROUP_ATTRIBUTES_SECTION = 4;
    private static final int GROUP_CLOSE_BRACKET = 5;
    private static final int GROUP_ATTRIBUTE_NAME = 1;
    private static final int GROUP_EQUAL_SYMBOL = 2;
    private static final int GROUP_ATTRIBUTE_VALUE = 3;

    private CodeArea caCodeArea;

    private ToolBar toolBar = null;

    public XMLEditor(String tabText, Path path) {
        super(tabText);

        this.caCodeArea = new CodeArea("");
        this.caCodeArea.setEditable(true);
        this.caCodeArea.setParagraphGraphicFactory(LineNumberFactory.get(caCodeArea));
        this.caCodeArea.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE);
        this.caCodeArea.getStylesheets().add(this.getClass().getResource("xml-highlighting.css").toExternalForm());
        this.caCodeArea.textProperty().addListener((obs, oldText, newText) -> {
            this.caCodeArea.setStyleSpans(0, computeHighlighting(newText));
        });
        addContextMenu(this.caCodeArea);

        try {
            this.caCodeArea.replaceText(FileUtils.readFileToString(path.toFile()));
        } catch (IOException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Error reading file.", ex);
        }

        BorderPane rootPane = new BorderPane();

        toolBar = createToolBar();
        rootPane.setTop(toolBar);
        rootPane.setCenter(caCodeArea);
        this.getTab().setContent(rootPane);

    }

    private StyleSpans<Collection<String>> computeHighlighting(String text) {
        Matcher matcher = XML_TAG.matcher(text);
        int lastKwEnd = 0;
        StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();
        while (matcher.find()) {
            spansBuilder.add(Collections.emptyList(), matcher.start() - lastKwEnd);
            if (matcher.group("COMMENT") != null) {
                spansBuilder.add(Collections.singleton("comment"), matcher.end() - matcher.start());
            } else {
                if (matcher.group("ELEMENT") != null) {
                    String attributesText = matcher.group(GROUP_ATTRIBUTES_SECTION);
                    spansBuilder.add(Collections.singleton("tagmark"),
                            matcher.end(GROUP_OPEN_BRACKET) - matcher.start(GROUP_OPEN_BRACKET));
                    spansBuilder.add(Collections.singleton("anytag"),
                            matcher.end(GROUP_ELEMENT_NAME) - matcher.end(GROUP_OPEN_BRACKET));
                    if (!attributesText.isEmpty()) {
                        lastKwEnd = 0;
                        Matcher amatcher = ATTRIBUTES.matcher(attributesText);
                        while (amatcher.find()) {
                            spansBuilder.add(Collections.emptyList(), amatcher.start() - lastKwEnd);
                            spansBuilder.add(Collections.singleton("attribute"),
                                    amatcher.end(GROUP_ATTRIBUTE_NAME) - amatcher.start(GROUP_ATTRIBUTE_NAME));
                            spansBuilder.add(Collections.singleton("tagmark"),
                                    amatcher.end(GROUP_EQUAL_SYMBOL) - amatcher.end(GROUP_ATTRIBUTE_NAME));
                            spansBuilder.add(Collections.singleton("avalue"),
                                    amatcher.end(GROUP_ATTRIBUTE_VALUE) - amatcher.end(GROUP_EQUAL_SYMBOL));
                            lastKwEnd = amatcher.end();
                        }
                        if (attributesText.length() > lastKwEnd)
                            spansBuilder.add(Collections.emptyList(), attributesText.length() - lastKwEnd);
                    }
                    lastKwEnd = matcher.end(GROUP_ATTRIBUTES_SECTION);
                    spansBuilder.add(Collections.singleton("tagmark"),
                            matcher.end(GROUP_CLOSE_BRACKET) - lastKwEnd);
                }
            }
            lastKwEnd = matcher.end();
        }
        spansBuilder.add(Collections.emptyList(), text.length() - lastKwEnd);
        return spansBuilder.create();
    }

    private void setCode(String code) {
        this.caCodeArea.replaceText(code);

    }

    private String getCode() {
        return this.caCodeArea.getText();
    }

    /**
     * Creates the toolBar for the editor.
     *
     * @return
     */

    private ToolBar createToolBar() {

        ToolBar toolBar = new ToolBar();
        toolBar.setOrientation(Orientation.HORIZONTAL);

        Button btnSave = GlyphsDude.createIconButton(MaterialDesignIcon.FLOPPY, "Save");
        btnSave.setOnAction(this::handleSaveButton);

        toolBar.getItems().add(btnSave);

        return toolBar;

    }

    private void handleSaveButton(ActionEvent event) {
        System.out.println("Event: " + event.getSource());
        @SuppressWarnings("unchecked")
        Map<String, String> map = (Map<String, String>) this.getTab().getUserData();
        String path = map.get("path");
        try {
            FileUtils.writeStringToFile(new File(path), caCodeArea.getText());
        } catch (IOException e) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, "Unable to save file.");
            Alert alert = new Alert(AlertType.ERROR);
            alert.setTitle("Oeps an error occured");
            alert.setHeaderText("Cannot save file. There went something wrong writing the file.");
            alert.setContentText(
                    "Please verify that your file is not read only, is not locked by other user or program, you have enough diskspace.");
            alert.showAndWait();
        }
    }

    public ToolBar getToolBar() {
        return toolBar;
    }

    public void setToolbar(ToolBar toolBar) {
        this.toolBar = toolBar;
    }

}