se.nawroth.asciidoc.browser.AsciidocBrowserApplication.java Source code

Java tutorial

Introduction

Here is the source code for se.nawroth.asciidoc.browser.AsciidocBrowserApplication.java

Source

/**
 * Licensed to Neo Technology under one or more contributor
 * license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright
 * ownership. Neo Technology 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 se.nawroth.asciidoc.browser;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import net.miginfocom.swing.MigLayout;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang3.StringEscapeUtils;
import org.fit.cssbox.swingbox.BrowserPane;

import bsh.util.JConsole;

@SuppressWarnings("serial")
public class AsciidocBrowserApplication extends JFrame {
    private static final int INITIAL_TOOLTIP_DELAY = 500;

    private static final String FILE_URI_SCHEMA = "file://";

    private static final String LINK_LINE_START = "include::";

    private static JDialog settingsDialog;

    private final JButton backButton, forwardButton, refreshButton, homebutton;

    private final JTextField locationTextField;

    private final JEditorPane sourceEditorPane;

    private final List<File> pageList = new ArrayList<File>();

    private File currentFile;
    private final JScrollPane treeScrollPane;
    private final JTree documentTree;
    private final JSplitPane treeSourceSplitPane;

    private final Map<CharSequence, CharSequence> replacements = new HashMap<CharSequence, CharSequence>();

    private final DefaultTreeModel documentModel = new DefaultTreeModel(null);

    private final Map<FileWrapper, Object[]> paths = new HashMap<FileWrapper, Object[]>();

    private TreePath currentSelectionPath;
    private final JTabbedPane documentTabbedPane;
    private final JScrollPane previewScrollPane;

    private final BrowserPane browserPane;

    private final AsciidocExecutor executor;
    private final JSplitPane sourceLogSplitPane;

    private final JConsole console;

    public AsciidocBrowserApplication(final String[] args) {
        super("Asciidoc Browser");
        setIconImage(Icons.APPLICATION.image());

        setSize(1200, 1024);

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(final WindowEvent e) {
                actionExit();
            }
        });

        JPanel buttonPanel = new JPanel();
        backButton = new JButton("");
        backButton.setIcon(Icons.BACK.icon());
        backButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                actionBack();
            }
        });
        buttonPanel.setLayout(new MigLayout("", "[1px][][][][]", "[1px]"));

        JButton btnOptionsbutton = new JButton("");
        btnOptionsbutton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                settingsDialog.setVisible(true);
            }
        });
        btnOptionsbutton.setIcon(Icons.OPTIONS.icon());
        buttonPanel.add(btnOptionsbutton, "flowx,cell 0 0");
        backButton.setEnabled(false);
        buttonPanel.add(backButton, "cell 0 0,grow");
        forwardButton = new JButton("");
        forwardButton.setIcon(Icons.FORWARD.icon());
        forwardButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                actionForward();
            }
        });
        forwardButton.setEnabled(false);
        buttonPanel.add(forwardButton, "cell 0 0,grow");
        getContentPane().setLayout(new MigLayout("", "[793.00px,grow]", "[44px][930px]"));
        getContentPane().add(buttonPanel, "cell 0 0,growx,aligny top");
        locationTextField = new JTextField(65);
        locationTextField.setText("");
        locationTextField.addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(final KeyEvent e) {
                int keyCode = e.getKeyCode();
                if (keyCode == KeyEvent.VK_ENTER || keyCode == KeyEvent.VK_TAB) {
                    actionGo();
                    refreshDocumentTree();
                }
            }
        });
        locationTextField.setTransferHandler(
                new TextFieldTransferHandler(locationTextField.getTransferHandler(), new Runnable() {
                    @Override
                    public void run() {
                        locationTextField.setText("");
                    }
                }, new Runnable() {

                    @Override
                    public void run() {
                        actionGo();
                        refreshDocumentTree();
                    }
                }));

        homebutton = new JButton("");
        homebutton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                locationTextField.setText(Settings.getHome());
                actionGo();
                refreshDocumentTree();
            }
        });

        refreshButton = new JButton("");
        refreshButton.setToolTipText("Refresh");
        refreshButton.setEnabled(false);
        refreshButton.setIcon(Icons.REFRESH.icon());
        refreshButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(final ActionEvent e) {
                actionGo();
                refreshPreview();
            }
        });
        buttonPanel.add(refreshButton, "cell 1 0");

        homebutton.setIcon(Icons.HOME.icon());
        buttonPanel.add(homebutton, "cell 2 0");
        buttonPanel.add(locationTextField, "cell 3 0,grow");

        treeSourceSplitPane = new JSplitPane();
        treeSourceSplitPane.setResizeWeight(0.3);
        getContentPane().add(treeSourceSplitPane, "cell 0 1,grow");

        treeScrollPane = new JScrollPane();
        treeScrollPane.setMinimumSize(new Dimension(200, 200));
        treeSourceSplitPane.setLeftComponent(treeScrollPane);

        documentTree = new DocumentTree(documentModel);
        documentTree.setCellRenderer(new TooltipsTreeCellRenderer());
        ToolTipManager.sharedInstance().registerComponent(documentTree);
        ToolTipManager.sharedInstance().setInitialDelay(INITIAL_TOOLTIP_DELAY);
        ToolTipManager.sharedInstance().setReshowDelay(0);
        documentTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(final TreeSelectionEvent tse) {
                TreePath newLeadSelectionPath = tse.getNewLeadSelectionPath();
                if (newLeadSelectionPath != null && !newLeadSelectionPath.equals(currentSelectionPath)) {
                    DefaultMutableTreeNode node = (DefaultMutableTreeNode) newLeadSelectionPath
                            .getLastPathComponent();
                    FileWrapper file = (FileWrapper) node.getUserObject();
                    showFile(file, true);
                    refreshPreview();
                }
            }
        });
        treeScrollPane.setViewportView(documentTree);

        sourceEditorPane = new JEditorPane();
        sourceEditorPane.setContentType("text/html");
        sourceEditorPane.setEditable(false);
        sourceEditorPane.addHyperlinkListener(new HandleHyperlinkUpdate());
        JScrollPane fileScrollPane = new JScrollPane(sourceEditorPane);
        fileScrollPane.setMinimumSize(new Dimension(600, 600));

        documentTabbedPane = new JTabbedPane(SwingConstants.BOTTOM);
        documentTabbedPane.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(final ChangeEvent ce) {
                refreshPreview();
            }
        });
        sourceLogSplitPane = new JSplitPane();
        sourceLogSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
        treeSourceSplitPane.setRightComponent(sourceLogSplitPane);
        sourceLogSplitPane.setTopComponent(documentTabbedPane);
        documentTabbedPane.add(fileScrollPane);
        documentTabbedPane.setTitleAt(0, "Source");

        browserPane = new BrowserPane();

        previewScrollPane = new JScrollPane(browserPane);
        documentTabbedPane.addTab("Preview", null, previewScrollPane, null);

        console = new JConsole();
        System.setErr(console.getErr());
        System.setOut(console.getOut());
        sourceLogSplitPane.setBottomComponent(console);

        executor = new AsciidocExecutor();
    }

    private void refreshPreview() {
        Component selectedComponent = documentTabbedPane.getSelectedComponent();
        if (selectedComponent != previewScrollPane) {
            return;
        }
        if (currentFile != null && currentFile.exists()) {
            console.println("Parsing AsciiDoc from '" + currentFile.getName() + "'.");
            File targetFile = executor.generate(currentFile);
            try {
                console.println(
                        "Loading the output from '" + currentFile.getName() + "' into the preview browser.");
                browserPane.setPage(targetFile.toURI().toURL());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private void actionExit() {
        settingsDialog.dispose();
        System.exit(0);
    }

    private void actionBack() {
        int pageIndex = pageList.indexOf(currentFile);
        int pageToShow = pageIndex - 1;
        if (pageToShow >= 0) {
            showFile(pageList.get(pageToShow), false);
        }
        refreshPreview();
    }

    private void actionForward() {
        int pageIndex = pageList.indexOf(currentFile);
        if (pageIndex != -1) {
            int pageToShow = pageIndex + 1;
            if (pageList.size() > pageToShow) {
                showFile(pageList.get(pageToShow), false);
            }
        }
        refreshPreview();
    }

    private void actionGo() {
        String location = locationTextField.getText().trim();
        if (location.startsWith(FILE_URI_SCHEMA) && location.length() > FILE_URI_SCHEMA.length()) {
            location = location.substring(FILE_URI_SCHEMA.length());
            locationTextField.setText(location);
        }
        File file = FileUtils.getFile(location);
        if (!file.exists()) {
            showError("File doesn't exist: " + file.getAbsolutePath());
            return;
        }
        if (file.isDirectory()) {
            showError("This is a directory, cannot open it: " + file);
            return;
        }
        if (!file.canRead()) {
            showError("Cannot read the file: " + file);
        }
        showFile(file, true);
        refreshButton.setEnabled(true);
    }

    private void showFile(final File file, final boolean addIt) {
        locationTextField.setText(file.getAbsolutePath());
        refreshReplacements();
        try {
            StringBuilder sb = new StringBuilder(10 * 1024);
            sb.append("<html><head><title>" + file.getName()
                    + "</title><style>body {font-size: 1em;}pre {margin: 0;}</style></head><body>");
            String parent = file.getParent();
            LineIterator lines = FileUtils.lineIterator(file, "UTF-8");
            while (lines.hasNext()) {
                String line = StringEscapeUtils.escapeHtml4(lines.next());
                sb.append("<pre>");
                if (line.startsWith(LINK_LINE_START)) {
                    String href = getFileLocation(parent, line);

                    sb.append("<a href=\"").append(href).append("\">").append(line).append("</a>");
                } else {
                    sb.append(line);
                }
                sb.append("</pre>");
            }
            sb.append("</body></html>");
            lines.close();

            sourceEditorPane.setText(sb.toString());
            sourceEditorPane.setCaretPosition(0);
            if (addIt) {
                int listSize = pageList.size();
                if (listSize > 0) {
                    int pageIndex = pageList.indexOf(currentFile);
                    if (pageIndex < listSize - 1) {
                        for (int i = listSize - 1; i > pageIndex; i--) {
                            pageList.remove(i);
                        }
                    }
                }
                pageList.add(file);
            }
            currentFile = file;
            updateButtons();
            if (paths.containsKey(file)) {
                currentSelectionPath = new TreePath(paths.get(file));
                documentTree.setSelectionPath(currentSelectionPath);
            }
        } catch (IOException e) {
            showError("Error in file handling: " + e);
        }
    }

    private void refreshDocumentTree() {
        paths.clear();
        FileWrapper.setSmartExclude(this.locationTextField.getText());
        FileWrapper root = new FileWrapper(this.locationTextField.getText());
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(root);
        Object[] rootPath = new Object[] { rootNode };
        paths.put(root, rootPath);
        addFileWrapperChildren(rootNode, rootPath);
        documentModel.setRoot(rootNode);
    }

    private void addFileWrapperChildren(final DefaultMutableTreeNode parentNode, final Object... parentPath) {
        FileWrapper parent = (FileWrapper) parentNode.getUserObject();
        for (FileWrapper child : getChildren(parent)) {
            DefaultMutableTreeNode node = new DefaultMutableTreeNode(child);
            parentNode.add(node);
            Object[] path = Arrays.copyOf(parentPath, parentPath.length + 1);
            path[parentPath.length] = node;
            paths.put(child, path);
            addFileWrapperChildren(node, path);
        }
    }

    private Collection<FileWrapper> getChildren(final se.nawroth.asciidoc.browser.FileWrapper parent) {
        List<FileWrapper> children = new ArrayList<FileWrapper>();
        String directory = parent.getParent();
        LineIterator lines;
        try {
            lines = FileUtils.lineIterator(parent, "UTF-8");
            while (lines.hasNext()) {
                String line = lines.next();
                if (line.startsWith(LINK_LINE_START)) {
                    String href = getFileLocation(directory, line);
                    FileWrapper fileWrapper = new FileWrapper(href);
                    if (fileWrapper.exists()) {
                        children.add(fileWrapper);
                    } else {
                        // System.out.println( href );
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return children;
    }

    private void refreshReplacements() {
        Properties properties = new Properties();
        try {
            properties.load(new StringReader(Settings.getConfiguration()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        replacements.clear();
        for (Entry<Object, Object> entry : properties.entrySet()) {
            if (entry.getKey() instanceof String && entry.getValue() instanceof String) {
                String key = (String) entry.getKey();
                String value = (String) entry.getValue();
                if (!key.startsWith("[") && !value.trim().isEmpty()) {
                    replacements.put("{" + key + "}", value);
                }
            }
        }
    }

    private String getFileLocation(final String parent, final String line) {
        int pos = line.indexOf("[");
        String href = line.substring(LINK_LINE_START.length(), pos);
        String hrefWithReplacements = href;
        for (Entry<CharSequence, CharSequence> entry : replacements.entrySet()) {
            hrefWithReplacements = hrefWithReplacements.replace(entry.getKey(), entry.getValue());
        }
        if (hrefWithReplacements.equals(href)) {
            href = parent + File.separator + href;
        } else {
            href = hrefWithReplacements;
        }
        return href;
    }

    private void showError(final String errorMessage) {
        JOptionPane.showMessageDialog(this, errorMessage, "Error", JOptionPane.ERROR_MESSAGE);
    }

    private void updateButtons() {
        if (pageList.size() < 2) {
            backButton.setEnabled(false);
            forwardButton.setEnabled(false);
        } else {
            int pageIndex = pageList.indexOf(currentFile);
            backButton.setEnabled(pageIndex > 0);
            forwardButton.setEnabled(pageIndex < (pageList.size() - 1));
        }
    }

    private class HandleHyperlinkUpdate implements HyperlinkListener {
        @Override
        public void hyperlinkUpdate(final HyperlinkEvent event) {
            HyperlinkEvent.EventType eventType = event.getEventType();
            if (eventType == HyperlinkEvent.EventType.ACTIVATED) {
                locationTextField.setText(event.getDescription());
                actionGo();
            }
        }
    }

    public static void main(final String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame.setDefaultLookAndFeelDecorated(true);
                    JDialog.setDefaultLookAndFeelDecorated(true);
                    System.setProperty("sun.awt.noerasebackground", "true");
                    UIManager.setLookAndFeel(new AsciidocBrowserSubstanceSkin());
                    AsciidocBrowserApplication browser = new AsciidocBrowserApplication(args);
                    browser.setVisible(true);
                    settingsDialog = new SettingsDialog();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}