com.costlowcorp.eriktools.jardetails.JarNavigationController.java Source code

Java tutorial

Introduction

Here is the source code for com.costlowcorp.eriktools.jardetails.JarNavigationController.java

Source

/*
 * 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 com.costlowcorp.eriktools.jardetails;

import com.costlowcorp.eriktools.ErikUtils;
import com.costlowcorp.eriktools.classdetails.ClassDetailsController;
import com.costlowcorp.eriktools.code.XMLEditor;
import com.costlowcorp.eriktools.files.image.ImageShowerController;
import com.costlowcorp.eriktools.toolentry.JavaPGrabber;
import com.costlowcorp.fx.utils.UIUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.URL;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.atomic.AtomicLong;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import javafx.application.Platform;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
import org.objectweb.asm.ClassReader;
import static org.objectweb.asm.Opcodes.ASM5;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.TraceClassVisitor;

/**
 * FXML Controller class
 *
 * @author ecostlow
 */
public class JarNavigationController implements Initializable {

    @FXML
    private TreeView<JarEntryTreeViewer> jarNavigation;

    @FXML
    private AnchorPane detailsPane;

    private File file;

    private WeakReference<Node> detailsNode;

    private ClassDetailsController classDetails;
    private Node classDetailsNode;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        final FXMLLoader classDetailsLoader = UIUtils.load(ClassDetailsController.class);
        this.classDetails = classDetailsLoader.getController();
        this.classDetailsNode = classDetailsLoader.getRoot();
        UIUtils.setAnchors(classDetailsNode, 0, 0, 0, 0);

        jarNavigation.setCellFactory((TreeView<JarEntryTreeViewer> param) -> new TextFieldTreeCellImpl());
        jarNavigation.getSelectionModel().selectedItemProperty()
                .addListener((ObservableValue<? extends TreeItem<JarEntryTreeViewer>> observable,
                        TreeItem<JarEntryTreeViewer> oldValue, TreeItem<JarEntryTreeViewer> newValue) -> {
                    if (newValue == null) {
                        return;
                    }
                    if (newValue == jarNavigation.getRoot()
                            || newValue.getValue().actualFileProperty().getValue().endsWith("/")) {
                        if (detailsNode.get() == null) {
                            getDetails();
                        } else {
                            detailsPane.getChildren().setAll(detailsNode.get());
                        }
                    } else {
                        final String filename = newValue.getValue().actualFileProperty().get();
                        final String extension = ErikUtils.getExtension(filename);
                        final Node end;
                        switch (extension) {
                        case "class":
                            end = classDetailsNode;
                            new Thread(() -> readClass(filename)).start();
                            break;
                        case "png":
                        case "jpg":
                        case "jpeg":
                        case "gif":
                            final FXMLLoader loader = UIUtils.load(ImageShowerController.class);
                            final ImageShowerController imageCtrl = loader.getController();
                            try (ZipFile zip = new ZipFile(file)) {
                                final ZipEntry entry = zip.getEntry(filename);
                                final InputStream in = zip.getInputStream(entry);
                                final Image image = new Image(in);
                                imageCtrl.populateWith(image);
                            } catch (IOException ex) {
                                Logger.getLogger(JarNavigationController.class.getName()).log(Level.SEVERE, null,
                                        ex);
                            }

                            end = loader.getRoot();
                            break;
                        case "properties":
                        case "mf":
                        case "txt":
                        case "css":
                            final TextArea txt = new TextArea();
                            end = txt;
                            new Thread(() -> plainText(filename, txt)).start();
                            break;
                        case "xml":
                        case "fxml":
                            end = XMLEditor.make(readPlainText(filename));
                            break;
                        default:
                            end = new Label("Unable to load reader for " + filename);
                        }
                        UIUtils.setAnchors(end, 0, 0, 0, 0);
                        Platform.runLater(() -> detailsPane.getChildren().setAll(end));

                    }
                });
    }

    public void populateWith(File file) {
        this.file = file;
        new Thread(() -> populate(file)).start();
        getDetails();
    }

    private void populate(File file) {
        try (ZipFile zip = new ZipFile(file)) {
            final Image folder = new Image("/folder.png");

            final TreeItem<JarEntryTreeViewer> root = new TreeItem<>(new JarEntryTreeViewer(zip.getName()),
                    new ImageView(folder));

            final Map<String, TreeItem> map = new HashMap<>();
            final List<TreeItem<JarEntryTreeViewer>> innerClassList = new ArrayList<>();
            final AtomicLong innerClassDepth = new AtomicLong();
            zip.stream().sorted((first, second) -> first.getName().compareToIgnoreCase(second.getName()))
                    .forEach(entry -> {
                        final String entryName = entry.getName();
                        final String parentDir = parentDir(entry);

                        final TreeItem<JarEntryTreeViewer> item = new TreeItem<>(new JarEntryTreeViewer(entryName));
                        if (entry.isDirectory()) {
                            item.setGraphic(new ImageView(folder));
                        }

                        final boolean parentFound = map.containsKey(parentDir);
                        if (parentFound) {
                            final String currentFilename = item.getValue().showNameProperty().get();
                            final long currentInnerClassDepth = currentFilename.chars().filter(ch -> ch == '$')
                                    .count();
                            if (currentInnerClassDepth == 0) {
                                final TreeItem parent = map.get(parentDir);
                                parent.getChildren().add(item);
                                item.getChildren().addAll(innerClassList);
                                innerClassList.clear();
                                ;
                            } else if (currentInnerClassDepth > innerClassDepth.get()) {
                                innerClassList.add(item);
                            } else {
                                if (currentInnerClassDepth != innerClassDepth.get()) {
                                    item.getChildren().addAll(innerClassList);
                                    innerClassList.clear();
                                }
                                innerClassList.add(item);
                            }
                            innerClassDepth.set(currentInnerClassDepth);

                        } else {
                            root.getChildren().add(item);
                        }
                        item.setExpanded(true);
                        map.put(entryName, item);
                    });
            root.setExpanded(true);

            Platform.runLater(() -> jarNavigation.setRoot(root));
        } catch (IOException ex) {
            Logger.getLogger(JarNavigationController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private static String parentDir(ZipEntry entry) {
        final String name = entry.getName();
        if (!name.contains("/")) {
            return name;
        }
        final int end = entry.isDirectory() ? name.lastIndexOf('/', name.length() - 2) : name.lastIndexOf('/');
        return end < 0 ? "/" : name.substring(0, end + 1);

    }

    private void getDetails() {
        new Thread(() -> {
            final FXMLLoader loader = UIUtils.load(JarDetailsController.class);
            final JarDetailsController controller = loader.getController();
            //controller.populateWith(file.);
            controller.populateWith(file);
            final Node details = loader.getRoot();
            this.detailsNode = new WeakReference<>(details);
            UIUtils.setAnchors(details, 0, 0, 0, 0);
            Platform.runLater(() -> detailsPane.getChildren().setAll(details));
        }).start();
    }

    private void readClass(String filename) {
        try (JarFile jar = new JarFile(this.file)) {
            final JarEntry entry = jar.getJarEntry(filename);
            final ClassNode node = new ClassNode(ASM5);
            final InputStream in = jar.getInputStream(entry);
            final ClassReader reader = new ClassReader(in);
            //reader.accept(node, ClassReader.EXPAND_FRAMES);

            final Printer asmifier = new ASMifier();
            //final InputStream in2 = jar.getInputStream(entry);
            final ByteArrayOutputStream bout = new ByteArrayOutputStream();

            final TraceClassVisitor tracer = new TraceClassVisitor(node, asmifier, new PrintWriter(bout));
            reader.accept(tracer, ClassReader.EXPAND_FRAMES);
            final String className = filename.replace('/', '.').substring(0, filename.length() - ".class".length());
            final String javap = JavaPGrabber.INSTANCE.run(file.toPath(), className);
            Platform.runLater(() -> {
                classDetails.populateClassSummary(node.name, filename, node.sourceFile, node.version,
                        node.superName, node.interfaces);
                classDetails.populateAsmifierTab(bout.toString());
                classDetails.populateJavaPTab(javap);
            });
        } catch (IOException ex) {
            Logger.getLogger(JarNavigationController.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private void plainText(String filename, TextArea node) {
        final String txt = readPlainText(filename);
        Platform.runLater(() -> node.setText(txt));
    }

    private String readPlainText(String filename) {
        try (JarFile jar = new JarFile(this.file)) {
            final JarEntry entry = jar.getJarEntry(filename);
            final byte[] bytes = new byte[2048];
            final ByteArrayOutputStream bout = new ByteArrayOutputStream();
            try (InputStream in = jar.getInputStream(entry)) {
                for (int length = in.read(bytes); length > 0; length = in.read(bytes)) {
                    bout.write(bytes, 0, length);
                }
            }
            return bout.toString();
        } catch (IOException ex) {
            Logger.getLogger(JarNavigationController.class.getName()).log(Level.SEVERE, null, ex);
            return ex.getMessage();
        }
    }
}