Als ich diese Lösung zur Darstellunbg und Analyse von beliebigen strukturierten Daten fand (das Beispiel erledigt das für ASN.1 kodierte Informationen), fasste ich den Entschluss, dass ich eine entsprechende Komponente in Java realisieren wollte.
Grob gesagt musste also eine Komponente her, die asu zwei Teilen bestand: einem Baum, der die Struktur darstellt und einem daneben befindlichen Element, das die Inhalte als Hexdump (hexadezimal kodierte Bytes) darstellt.
Zwischen beiden Teilen sollte ein Austausch stattfinden: Klickt man einen Knoten des Baums an, werden rechts alle Bytes hervorgehoben, die zu diesem Knoten gehören und klickt man rechts auf ein Byte, so wird der entsprechende Knoten im Baum geöffnet und selektiert.
Dazu musste ein entsprechendes TreeModel geschaffen werden. Für die Darstellung des Baumes benutze ich einen ganz normalen JTree. Für die Anzeige des HexDump kommt ein JTextArea zum Einsatz, in dem man über entsprechende Attribute seines StyledDocument bestimmte Abschnitte hervorheben kann.
Zusammen sieht das zum Beispiel so aus:
Beispiel, das die Kodierung eines zufälligen UTF-Strings zeigt
Man sieht, dass ich noch ein wenig von den eingangs formulierten Zielen abgewichen bin: der selektierte Knoten im Baum wird in Pink hervorgehoben und sein unmittelbar übergeordneter Knoten in Gelb.
Dioe Implementierung gebe ich hier an:
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
public class HexInspector extends javax.swing.JPanel implements java.awt.event.ComponentListener
, javax.swing.event.TreeSelectionListener
, java.awt.event.MouseListener
{
private javax.swing.JTextPane ePane;
private javax.swing.JScrollPane scroller;
private de.elbosso.model.tree.ByteArrayNode root;
private javax.swing.JTree tree;
public HexInspector(de.elbosso.model.tree.ByteArrayNode node)
{
super(new java.awt.GridLayout(1,2));
ePane=new javax.swing.JTextPane();
ePane.setEditable(false);
ePane.setFont(new java.awt.Font(java.awt.Font.MONOSPACED, java.awt.Font.PLAIN, ePane.getFont().getSize()));
ePane.setDocument(new javax.swing.text.DefaultStyledDocument());
ePane.addMouseListener(this);
scroller=new javax.swing.JScrollPane(ePane);
scroller.setHorizontalScrollBarPolicy(scroller.HORIZONTAL_SCROLLBAR_NEVER);
scroller.setVerticalScrollBarPolicy(scroller.VERTICAL_SCROLLBAR_ALWAYS);
scroller.addComponentListener(this);
root=node;
tree=new javax.swing.JTree(new javax.swing.tree.DefaultTreeModel(root));
tree.addTreeSelectionListener(this);
tree.setEditable(false);
javax.swing.JScrollPane scr=new javax.swing.JScrollPane(tree);
add(scr);
add(scroller);
}
public void setModel(de.elbosso.model.tree.ByteArrayNode root)
{
this.root=root;
tree.setModel(new javax.swing.tree.DefaultTreeModel(root));
ePane.setText(breakDown((ePane.getVisibleRect().width)/ePane.getFontMetrics(ePane.getFont()).charWidth('M')-1));
}
public void addTreeSelectionListener(javax.swing.event.TreeSelectionListener l)
{
tree.addTreeSelectionListener(l);
}
public void removeTreeSelectionListener(javax.swing.event.TreeSelectionListener l)
{
tree.removeTreeSelectionListener(l);
}
public void removeTreeSelectionListener(javax.swing.tree.TreeCellRenderer r)
{
tree.setCellRenderer(r);
}
public java.lang.String breakDown(int charsPerLine)
{
return new java.lang.String(de.elbosso.util.Utilities.formatHexDumpAsChars(root.getContent(), ' ', charsPerLine));
}
public static byte[] charToBytes(final char x) throws java.io.UnsupportedEncodingException
{
return charToBytes(x,java.nio.charset.Charset.defaultCharset());
}
public static byte[] charToBytes(final char x, java.nio.charset.Charset charset) throws java.io.UnsupportedEncodingException
{
String temp = new String(new char[] {x});
return temp.getBytes(charset);
}
@Override
public void componentResized(ComponentEvent e)
{
ePane.setText(breakDown((ePane.getVisibleRect().width)/ePane.getFontMetrics(ePane.getFont()).charWidth('M')-1));
}
@Override
public void componentMoved(ComponentEvent e)
{
}
@Override
public void componentShown(ComponentEvent e)
{
}
@Override
public void componentHidden(ComponentEvent e)
{
}
@Override
public void valueChanged(TreeSelectionEvent e)
{
clearAttributes();
if(e.isAddedPath())
{
de.elbosso.model.tree.ByteArrayNode ban=(de.elbosso.model.tree.ByteArrayNode)e.getPath().getParentPath().getLastPathComponent();
highlight(ban,true,java.awt.Color.YELLOW);
ban=(de.elbosso.model.tree.ByteArrayNode)e.getPath().getLastPathComponent();
highlight(ban,true,java.awt.Color.PINK);
}
}
@Override
public void mouseClicked(MouseEvent e)
{
int byteIndex=ePane.viewToModel(e.getPoint())/3;
javax.swing.tree.TreeNode target=getNodeForByte(root,byteIndex);
javax.swing.tree.TreePath tp=toTreePath(target);
expandNode(tree.getModel(),tp.getLastPathComponent(),tp);
tree.scrollPathToVisible(tp);
tree.getSelectionModel().setSelectionPath(tp);
}
private void clearAttributes()
{
javax.swing.text.StyledDocument sd=(javax.swing.text.StyledDocument)ePane.getDocument();
MutableAttributeSet attr = new SimpleAttributeSet();
sd.setCharacterAttributes(0,root.getContent().length*3,attr,true);
}
private void highlight(de.elbosso.model.tree.ByteArrayNode node, boolean bold, java.awt.Color color)
{
javax.swing.text.StyledDocument sd=(javax.swing.text.StyledDocument)ePane.getDocument();
MutableAttributeSet attr = new SimpleAttributeSet();
StyleConstants.setBold (attr, bold);
StyleConstants.setBackground(attr,color);
sd.setCharacterAttributes(node.getStartOffset()*3,node.getBateLength()*3-1,attr,true);
}
private javax.swing.tree.TreePath toTreePath(javax.swing.tree.TreeNode node)
{
java.util.List<javax.swing.tree.TreeNode> l=new java.util.LinkedList();
l.add(node);
while(node.getParent()!=null)
{
node=node.getParent();
l.add(node);
}
java.util.Collections.reverse(l);
javax.swing.tree.TreePath tp=new javax.swing.tree.TreePath(l.toArray());
return tp;
}
protected void expandNode(javax.swing.tree.TreeModel m,java.lang.Object o,javax.swing.tree.TreePath p)
{
if(m!=null)
{
tree.expandPath(p);
if(p.getParentPath()!=null)
{
expandNode(m,o,p.getParentPath());
}
}
}
private de.elbosso.model.tree.ByteArrayNode getNodeForByte(de.elbosso.model.tree.ByteArrayNode current,int byteIndex)
{
de.elbosso.model.tree.ByteArrayNode rv=current;
if(current.isLeaf()==false)
{
java.util.Enumeration en=current.children();
while(en.hasMoreElements())
{
de.elbosso.model.tree.ByteArrayNode ban=(de.elbosso.model.tree.ByteArrayNode)en.nextElement();
if((ban.getStartOffset()<=byteIndex)&&(ban.getStartOffset()+ban.getBateLength()>byteIndex))
{
rv=getNodeForByte(ban, byteIndex);
break;
}
}
}
return rv;
}
@Override
public void mousePressed(MouseEvent e)
{
}
@Override
public void mouseReleased(MouseEvent e)
{
}
@Override
public void mouseEntered(MouseEvent e)
{
}
@Override
public void mouseExited(MouseEvent e)
{
}
}
Die wichtige Klasse ByteArrayNode ist hier zu finden:
import javax.swing.tree.TreeNode;
public class ByteArrayNode extends de.elbosso.model.tree.CustomNode
{
private byte[] content;
private int startOffset;
protected java.util.List<ByteArrayNode> children;
public ByteArrayNode(int startOffset,java.lang.String content, javax.swing.tree.TreeNode parent)
{
super(content,parent);
this.startOffset=startOffset;
this.content=content.getBytes();
children=new java.util.LinkedList();
}
public int getStartOffset()
{
return startOffset;
}
public int getBateLength()
{
return this.content.length;
}
public byte[] getContent()
{
return content;
}
@Override
public TreeNode getChildAt(int index)
{
return children.get(index);
}
@Override
public boolean isLeaf()
{
return children.isEmpty();
}
@Override
public int getChildCount()
{
return children.size();
}
@Override
public boolean getAllowsChildren()
{
return !isLeaf();
}
@Override
public java.util.Enumeration children()
{
// System.out.println("++++++++++++++");
return java.util.Collections.enumeration(children);
}
}
Precision Time Protocol
15.10.2021
Durch meinen Neffen wurde ich neulich auf ein Video im Internet aufmerksam gemacht, aus dem ich von der Existenz des Protokolls PTP oder Precision Time Protocol erfuhr. Hier einige Gedanken dazu
WeiterlesenAI und ML Android Basteln C und C++ Chaos Datenbanken Docker dWb+ ESP Wifi Garten Geo Go GUI Hardware Java Jupyter JupyterBinder Komponenten Links Linux Markdown Markup Music Numerik OpenSource PKI-X.509-CA Präsentationen Python QBrowser Rants Raspi Revisited Security Software-Test sQLshell TeleGrafana Verschiedenes Video Virtualisierung Windows Upcoming...
Eine neue Version der sQLshell ist verfügbar!
WeiterlesenEin Influenzer würde einen solchen Artikel beginnen mit "Ich werde immer wieder gefragt, warum..." Da ich keiner bin kann ich so etwas nicht tun – statt dessen erläutere ich einfach das Szenario: Ich pflege diverse Open Source Projekte, die sich mit PKI und kryptographischen Timestamps. Jedoch benutzt die sQLshell elektronische Signaturen nur für die Verteilung der diversen Plugins.
WeiterlesenIch habe bisher einen Aspekt absichtlich und gewissenhaft aus der sQLshell herausgelassen, mich aber jetzt - wo die Zahl der gefundenen Sicherheitslücken in allen möglichen Produkten immer mehr zunimmt - doch einmal damit beschäftigt...
WeiterlesenManche nennen es Blog, manche Web-Seite - ich schreibe hier hin und wieder über meine Erlebnisse, Rückschläge und Erleuchtungen bei meinen Hobbies.
Wer daran teilhaben und eventuell sogar davon profitieren möchte, muss damit leben, daß ich hin und wieder kleine Ausflüge in Bereiche mache, die nichts mit IT, Administration oder Softwareentwicklung zu tun haben.
Ich wünsche allen Lesern viel Spaß und hin und wieder einen kleinen AHA!-Effekt...
PS: Meine öffentlichen Codeberg-Repositories findet man hier.