/** * erlyberly, erlang trace debugger * Copyright (C) 2016 Andy Till * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package erlyberly; import java.io.IOException; import java.util.Map; import com.ericsson.otp.erlang.OtpErlangAtom; import com.ericsson.otp.erlang.OtpErlangBinary; import com.ericsson.otp.erlang.OtpErlangException; import com.ericsson.otp.erlang.OtpErlangFun; import com.ericsson.otp.erlang.OtpErlangList; import com.ericsson.otp.erlang.OtpErlangObject; import com.ericsson.otp.erlang.OtpErlangTuple; import com.ericsson.otp.erlang.OtpErlangMap; import erlyberly.format.TermFormatter; import erlyberly.node.OtpUtil; import hextstar.HexstarView; import javafx.event.ActionEvent; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import javafx.scene.input.KeyCombination; @SuppressWarnings("rawtypes") public class TermTreeView extends TreeView<TermTreeItem> { private static final OtpErlangAtom DICT_ATOM = OtpUtil.atom("dict"); private static final OtpErlangAtom ERLYBERLY_RECORD_FIELD_ATOM = OtpUtil.atom("erlyberly_record_field"); public TermTreeView() { getStyleClass().add("term-tree"); setRoot(new TreeItem<TermTreeItem>()); MenuItem copyMenuItem = new MenuItem("Copy"); copyMenuItem.setAccelerator(KeyCombination.keyCombination("shortcut+c")); copyMenuItem.setOnAction(this::onCopyCalls); MenuItem dictMenuItem = new MenuItem("Dict to List"); dictMenuItem.setOnAction(this::onViewDict); MenuItem hexViewMenuItem = new MenuItem("Hex View"); hexViewMenuItem.setOnAction(this::onHexView); MenuItem decompileFunContextMenu = new MenuItem("Decompile Fun"); decompileFunContextMenu.setOnAction(this::onDecompileFun); setContextMenu(new ContextMenu(copyMenuItem, dictMenuItem, decompileFunContextMenu, hexViewMenuItem)); } public void onHexView(ActionEvent e) { TreeItem<TermTreeItem> item = getSelectionModel().getSelectedItem(); if(item != null && item.getValue().getObject() instanceof OtpErlangBinary) { OtpErlangBinary binary = (OtpErlangBinary) item.getValue().getObject(); HexstarView hexstarView; hexstarView = new HexstarView(); hexstarView.setBinary(binary); ErlyBerly.showPane("Hex View", ErlyBerly.wrapInPane(hexstarView)); } } public void onDecompileFun(ActionEvent e) { TreeItem<TermTreeItem> item = getSelectionModel().getSelectedItem(); if(item != null && item.getValue().getObject() instanceof OtpErlangFun) { OtpErlangFun fun = (OtpErlangFun)item.getValue().getObject(); try { String funSource = ErlyBerly.nodeAPI().decompileFun(fun); ErlyBerly.showPane(fun + " Source Code", ErlyBerly.wrapInPane(new CodeView(funSource))); } catch (OtpErlangException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } } public void onViewDict(ActionEvent e) { TreeItem<TermTreeItem> item = getSelectionModel().getSelectedItem(); if(item == null) return; if(item.getValue() == null || !DICT_ATOM.equals(item.getValue().getObject())) return; if(item.getParent() == null || !(item.getParent().getValue().getObject() instanceof OtpErlangTuple)) return; OtpErlangObject dict = item.getParent().getValue().getObject(); try { OtpErlangList props = ErlyBerly.nodeAPI().dictToPropslist(dict); TermTreeView parentControl = new TermTreeView(); parentControl.populateFromTerm(props); ErlyBerly.showPane("dict_to_list", ErlyBerly.wrapInPane(parentControl)); } catch (Exception e1) { e1.printStackTrace(); } } public void populateFromListContents(OtpErlangList list) { for (OtpErlangObject a : list) { populateFromTerm(a); } } public void populateFromTerm(OtpErlangObject obj) { setShowRoot(false); addToTreeItem(getRoot(), obj); } private void addToTreeItem(TreeItem<TermTreeItem> parent, OtpErlangObject obj) { TermFormatter f = ErlyBerly.getTermFormatter(); if(obj instanceof OtpErlangBinary) { String termString = f.toString(obj); TreeItem<TermTreeItem> item = new TreeItem<>(new TermTreeItem(obj, termString)); parent.getChildren().add(item); } else if(obj instanceof OtpErlangTuple) { OtpErlangObject[] elements = ((OtpErlangTuple) obj).elements(); if(elements.length == 0) { parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.emptyTupleString()))); } else { TreeItem<TermTreeItem> tupleItem; if(OtpUtil.isErlyberlyRecord(obj)) { String recordNameText = "#" + OtpUtil.tupleElement(1, obj) + " "; tupleItem = new TreeItem<>(new TermTreeItem(obj, f.tupleLeftParen())); tupleItem.setGraphic(recordLabel(recordNameText)); parent.getChildren().add(tupleItem); tupleItem.setExpanded(true); elements = OtpUtil.iterableElements(OtpUtil.tupleElement(2, obj)); for (OtpErlangObject e : elements) { addToTreeItem(tupleItem, e); } parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.tupleRightParen()))); } else if(isRecordField(obj)) { tupleItem = new TreeItem<>(new TermTreeItem(obj, " ")); tupleItem.setGraphic(recordLabel(OtpUtil.tupleElement(1, obj) + " = ")); tupleItem.setExpanded(true); parent.getChildren().add(tupleItem); OtpErlangObject value = OtpUtil.tupleElement(2, obj); if(OtpUtil.isLittleTerm(value)) tupleItem.setValue(new TermTreeItem(value, f.toString(obj))); else addToTreeItem(tupleItem, value); } else { tupleItem = new TreeItem<>(); tupleItem.setExpanded(true); if(OtpUtil.isLittleTerm(obj)) { tupleItem.setValue(new TermTreeItem(obj, f.toString(obj))); parent.getChildren().add(tupleItem); } else { tupleItem.setValue(new TermTreeItem(obj, f.tupleLeftParen())); for (OtpErlangObject e : elements) { addToTreeItem(tupleItem, e); } parent.getChildren().add(tupleItem); parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.tupleRightParen()))); } } } } else if(obj instanceof OtpErlangList) { OtpErlangObject[] elements = ((OtpErlangList) obj).elements(); if(elements.length == 0) { parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.emptyListString()))); } else { TreeItem<TermTreeItem> listItem; listItem = new TreeItem<>(new TermTreeItem(obj, f.listLeftParen())); listItem.setExpanded(true); if(OtpUtil.isLittleTerm(obj)) { listItem.setValue(new TermTreeItem(obj, f.toString(obj))); parent.getChildren().add(listItem); } else { listItem.setValue(new TermTreeItem(obj, f.listLeftParen())); for (OtpErlangObject e : elements) { addToTreeItem(listItem, e); } if (!((OtpErlangList)obj).isProper()) { listItem.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.cons()))); addToTreeItem(listItem, ((OtpErlangList)obj).getLastTail()); } parent.getChildren().add(listItem); parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.listRightParen()))); } } } else if (obj instanceof OtpErlangMap){ TreeItem<TermTreeItem> mapNode = new TreeItem<>(new TermTreeItem(obj, f.mapLeft(obj))); parent.getChildren().add(mapNode); for (Map.Entry<OtpErlangObject,OtpErlangObject> e : ((OtpErlangMap) obj).entrySet()) { if (f.isHiddenField(e.getKey())) continue; String keyStr = f.mapKeyToString(e.getKey()); String valStr = f.toString(e.getValue()); if (valStr.length() < 50) { TreeItem<TermTreeItem> key = new TreeItem<>(new TermTreeItem(e.getKey(), valStr)); key.setGraphic(recordLabel(keyStr)); mapNode.getChildren().add(key); } else { TreeItem<TermTreeItem> key = new TreeItem<>(new TermTreeItem(e.getKey(), "")); key.setGraphic(recordLabel(keyStr)); addToTreeItem(key, e.getValue()); mapNode.getChildren().add(key); } } parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.mapRight()))); } else { parent.getChildren().add(new TreeItem<>(new TermTreeItem(obj, f.toString(obj)))); } } private Label recordLabel(String recordNameText) { Label label; label = new Label(recordNameText); label.getStyleClass().add("record-label"); return label; } private boolean isRecordField(OtpErlangObject obj) { return OtpUtil.isTupleTagged(ERLYBERLY_RECORD_FIELD_ATOM, obj); } private void onCopyCalls(ActionEvent e) { StringBuilder sbuilder = new StringBuilder(); for (TreeItem item : getSelectionModel().getSelectedItems()) { copyTerms(item, sbuilder); } } private void copyTerms(TreeItem item, StringBuilder sbuilder) { for (Object obj : item.getChildren()) { copyTerms((TreeItem) obj, sbuilder); } copyToClipboard(sbuilder); } private void copyToClipboard(StringBuilder sbuilder) { final Clipboard clipboard = Clipboard.getSystemClipboard(); final ClipboardContent content = new ClipboardContent(); content.putString(sbuilder.toString()); clipboard.setContent(content); } }