/*- * Copyright (C) 2007 Erik Larsson * * 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 org.catacombae.hfsexplorer.gui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.io.PrintStream; import java.util.LinkedList; import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.event.TreeWillExpandListener; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.ExpandVetoException; import javax.swing.tree.TreePath; import org.catacombae.csjc.PrintableStruct; import org.catacombae.csjc.StructElements; import org.catacombae.csjc.structelements.Dictionary; import org.catacombae.hfs.BTreeFile; import org.catacombae.hfsexplorer.FileSystemBrowser.NoLeafMutableTreeNode; import org.catacombae.hfs.types.hfscommon.CommonBTNode; import org.catacombae.hfs.types.hfscommon.CommonBTNodeDescriptor; import org.catacombae.hfsexplorer.io.JTextAreaOutputStream; import org.catacombae.util.Util.Pair; /** * @author <a href="http://www.catacombae.org/" target="_top">Erik Larsson</a> */ public abstract class BTreeInfoPanel<R, B extends BTreeFile> extends javax.swing.JPanel { private static final int UNIT_INCREMENT = 10; protected final String INDEX_NAME = "index"; protected final String LEAF_NAME = "leaf"; protected final String PRINT_FIELDS_AREA_NAME = "printfieldsarea"; protected final String OTHER_NAME = "other"; protected final String STRUCT_VIEW_PANEL_NAME = "structview"; protected final JPanel leafPanel; protected final CardLayout clLeaf; /** Creates new form BTreeInfoPanel */ public BTreeInfoPanel(final B bTree) { initComponents(); descriptionLabel.setText(getHeaderText()); // Populate the root /* * What we need is a method that gets us the children of the "current" node. * A B-tree starts with a header node, */ CommonBTNode iNode = bTree.getRootNode(); // Get root index node. if(iNode == null) { DefaultTreeModel model = new DefaultTreeModel(new NoLeafMutableTreeNode("<empty>")); bTreeFileTree.setModel(model); leafPanel = null; clLeaf = null; return; } DefaultMutableTreeNode rootNode = new NoLeafMutableTreeNode(new BTNodeStorage( bTree.getRootNodeNumber(), iNode, getRootNodeName())); expandNode(rootNode, iNode, bTree); DefaultTreeModel model = new DefaultTreeModel(rootNode); bTreeFileTree.setModel(model); bTreeFileTree.addTreeWillExpandListener(new TreeWillExpandListener() { public void treeWillExpand(TreeExpansionEvent e) throws ExpandVetoException { TreePath tp = e.getPath(); Object obj = tp.getLastPathComponent(); if(obj instanceof DefaultMutableTreeNode) { DefaultMutableTreeNode dmtn = ((DefaultMutableTreeNode) obj); Object obj2 = dmtn.getUserObject(); if(obj2 instanceof BTNodeStorage) { CommonBTNode node = ((BTNodeStorage) obj2).getNode(); expandNode(dmtn, node, bTree); } else throw new RuntimeException("Wrong user object type in expandable node!"); } else throw new RuntimeException("Wrong node type in tree!"); } public void treeWillCollapse(TreeExpansionEvent e) { } }); final CardLayout clRoot = new CardLayout(); leafPanel = new JPanel(); clLeaf = new CardLayout(); leafPanel.setLayout(clLeaf); leafPanel.add(new JLabel("INTERNAL ERROR!", SwingConstants.CENTER), OTHER_NAME); final JScrollPane structViewPanelScroller = new JScrollPane(); structViewPanelScroller.getVerticalScrollBar().setUnitIncrement(UNIT_INCREMENT); structViewPanelScroller.getHorizontalScrollBar().setUnitIncrement(UNIT_INCREMENT); leafPanel.add(structViewPanelScroller, STRUCT_VIEW_PANEL_NAME); LinkedList<Pair<JPanel, String>> customPanelsList = new LinkedList<Pair <JPanel, String>>(); createCustomPanels(customPanelsList); for(Pair<JPanel, String> panel : customPanelsList) { JScrollPane customPanelScroller = new JScrollPane(panel.getA()); customPanelScroller.getVerticalScrollBar().setUnitIncrement( UNIT_INCREMENT); customPanelScroller.getHorizontalScrollBar().setUnitIncrement( UNIT_INCREMENT); leafPanel.add(customPanelScroller, panel.getB()); } final LeafInfoPanel printFieldsTextAreaHeader = new LeafInfoPanel(); final JTextArea printFieldsTextArea = new JTextArea(0, 0); printFieldsTextArea.setEditable(false); printFieldsTextArea.setLineWrap(false); printFieldsTextArea.setOpaque(false); final JPanel printFieldsTextAreaContainer = new JPanel(new BorderLayout()); printFieldsTextAreaContainer.add(printFieldsTextAreaHeader, BorderLayout.NORTH); printFieldsTextAreaContainer.add(printFieldsTextArea, BorderLayout.CENTER); JScrollPane printFieldsTextAreaScroller = new JScrollPane(printFieldsTextAreaContainer); printFieldsTextAreaScroller.getVerticalScrollBar().setUnitIncrement(UNIT_INCREMENT); printFieldsTextAreaScroller.getHorizontalScrollBar().setUnitIncrement(UNIT_INCREMENT); leafPanel.add(printFieldsTextAreaScroller, PRINT_FIELDS_AREA_NAME); infoPanel.setLayout(clRoot); final JLabel indexNodeLabel = new JLabel("No selection.", SwingConstants.CENTER); infoPanel.add(indexNodeLabel, INDEX_NAME); infoPanel.add(leafPanel, LEAF_NAME); //infoScroller.setViewportView(infoPanel); bTreeFileTree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent te) { //System.err.println("Tree selection"); Object o = te.getPath().getLastPathComponent(); if(o instanceof DefaultMutableTreeNode) { Object o2 = ((DefaultMutableTreeNode) o).getUserObject(); if(o2 instanceof BTNodeStorage) { final BTNodeStorage nodeStorage = (BTNodeStorage) o2; final CommonBTNode btn = nodeStorage.getNode(); CommonBTNodeDescriptor btnd = btn.getNodeDescriptor(); switch(btnd.getNodeType()) { case INDEX: indexNodeLabel.setText("Index node " + nodeStorage.getNodeNumber() + " " + "with " + btnd.getNumberOfRecords() + " " + "records."); break; case LEAF: indexNodeLabel.setText("Leaf node " + nodeStorage.getNodeNumber() + " with " + btnd.getNumberOfRecords() + " " + "records."); break; default: indexNodeLabel.setText("Unknown error!"); } clRoot.show(infoPanel, INDEX_NAME); } else { final BTLeafStorage leafStorage = (BTLeafStorage) o2; final R rec = leafStorage.getRecord(); if(handleLeafRecord(leafStorage)); else if(rec instanceof StructElements) { Dictionary dict = ((StructElements) rec).getStructElements(); String label = dict.getTypeDescription(); if(label == null) label = dict.getTypeName(); final LeafInfoPanel leafInfoPanel = new LeafInfoPanel(); leafInfoPanel.setRecordNumber( leafStorage.getRecordNumber()); leafInfoPanel.setRecordOffset( leafStorage.getRecordOffset()); leafInfoPanel.setRecordSize( leafStorage.getRecordSize()); final JPanel containerPanel = new JPanel(new BorderLayout()); containerPanel.add(leafInfoPanel, BorderLayout.NORTH); containerPanel.add( new StructViewPanel(label + ":", dict), BorderLayout.CENTER); structViewPanelScroller.setViewportView( containerPanel); clLeaf.show(leafPanel, STRUCT_VIEW_PANEL_NAME); } else if(rec instanceof PrintableStruct) { PrintStream ps = new PrintStream(new JTextAreaOutputStream(System.err, printFieldsTextArea)); printFieldsTextAreaHeader.setRecordNumber( leafStorage.getRecordNumber()); printFieldsTextAreaHeader.setRecordOffset( leafStorage.getRecordOffset()); printFieldsTextAreaHeader.setRecordSize( leafStorage.getRecordSize()); printFieldsTextArea.setText(""); ((PrintableStruct) rec).print(ps, ""); ps.close(); printFieldsTextArea.setCaretPosition(0); clLeaf.show(leafPanel, PRINT_FIELDS_AREA_NAME); } else { System.err.println("BTreeInfoPanel: Could not show record type " + rec.getClass()); clLeaf.show(leafPanel, OTHER_NAME); } clRoot.show(infoPanel, LEAF_NAME); } } else System.err.println("WARNING: Unknown node type in " + "bTreeFileTree - " + o.getClass().toString()); } }); } public abstract String getRootNodeName(); public abstract String getHeaderText(); public abstract void createCustomPanels( List<Pair<JPanel, String>> customPanelsList); public abstract void expandNode(DefaultMutableTreeNode dmtn, CommonBTNode node, B bTree); public abstract boolean handleLeafRecord(BTLeafStorage leafStorage); protected static class BTNodeStorage { private long nodeNumber; private CommonBTNode node; private String text; public BTNodeStorage(long nodeNumber, CommonBTNode node, String text) { this.nodeNumber = nodeNumber; this.node = node; this.text = text; } public long getNodeNumber() { return nodeNumber; } public CommonBTNode getNode() { return node; } @Override public String toString() { return text; } } protected class BTLeafStorage { private final int recordNumber; private final int recordOffset; private final int recordSize; private R rec; private String text; public BTLeafStorage(int recordNumber, int recordOffset, int recordSize, R rec, String text) { this.recordNumber = recordNumber; this.recordOffset = recordOffset; this.recordSize = recordSize; this.rec = rec; this.text = text; } public int getRecordNumber() { return recordNumber; } public int getRecordOffset() { return recordOffset; } public int getRecordSize() { return recordSize; } public R getRecord() { return rec; } @Override public String toString() { return text; } } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents private void initComponents() { jSplitPane1 = new javax.swing.JSplitPane(); jPanel1 = new javax.swing.JPanel(); bTreeFileTreeScroller = new javax.swing.JScrollPane(); bTreeFileTree = new javax.swing.JTree(); infoPanel = new javax.swing.JPanel(); descriptionLabel = new javax.swing.JLabel(); jSplitPane1.setDividerLocation(330); jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); jSplitPane1.setResizeWeight(1.0); bTreeFileTreeScroller.setViewportView(bTreeFileTree); org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(bTreeFileTreeScroller, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 359, Short.MAX_VALUE) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(org.jdesktop.layout.GroupLayout.TRAILING, bTreeFileTreeScroller, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 329, Short.MAX_VALUE) ); jSplitPane1.setLeftComponent(jPanel1); infoPanel.setPreferredSize(new java.awt.Dimension(100, 140)); org.jdesktop.layout.GroupLayout infoPanelLayout = new org.jdesktop.layout.GroupLayout(infoPanel); infoPanel.setLayout(infoPanelLayout); infoPanelLayout.setHorizontalGroup( infoPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(0, 359, Short.MAX_VALUE) ); infoPanelLayout.setVerticalGroup( infoPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(0, 101, Short.MAX_VALUE) ); jSplitPane1.setRightComponent(infoPanel); descriptionLabel.setText("View of the B*-tree:"); org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .addContainerGap() .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jSplitPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 361, Short.MAX_VALUE) .add(descriptionLabel)) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(layout.createSequentialGroup() .addContainerGap() .add(descriptionLabel) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(jSplitPane1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 437, Short.MAX_VALUE) .addContainerGap()) ); }// </editor-fold>//GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables public javax.swing.JTree bTreeFileTree; private javax.swing.JScrollPane bTreeFileTreeScroller; private javax.swing.JLabel descriptionLabel; private javax.swing.JPanel infoPanel; private javax.swing.JPanel jPanel1; private javax.swing.JSplitPane jSplitPane1; // End of variables declaration//GEN-END:variables }