Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
HexView.java
Go to the documentation of this file.
1/*
2 * Autopsy
3 *
4 * Copyright 2019 Basis Technology Corp.
5 * Contact: carrier <at> sleuthkit <dot> org
6 *
7 * Copyright 2013 Willi Ballenthin
8 * Contact: willi.ballenthin <at> gmail <dot> com
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19 * See the License for the specific language governing permissions and
20 * limitations under the License.
21 */
22package org.sleuthkit.autopsy.rejview;
23
24import java.awt.BorderLayout;
25import java.awt.Dimension;
26import java.awt.Font;
27import javax.swing.border.BevelBorder;
28import javax.swing.event.CaretEvent;
29import javax.swing.event.CaretListener;
30import javax.swing.text.BadLocationException;
31import javax.swing.text.DefaultHighlighter;
32import javax.swing.text.JTextComponent;
33import java.nio.ByteBuffer;
34import java.util.logging.Level;
35import javax.swing.BoxLayout;
36import javax.swing.JLabel;
37import javax.swing.JPanel;
38import javax.swing.JScrollPane;
39import javax.swing.JSplitPane;
40import javax.swing.JTextArea;
41import javax.swing.SwingConstants;
42import org.openide.util.NbBundle.Messages;
43import org.sleuthkit.autopsy.coreutils.Logger;
44
53final class HexView extends JPanel {
54
55 private final static int DEFAULT_BYTES_PER_LINE = 0x10;
56 private final static char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
57 private final static int CHAR_ARRAY_SIZE = 3;
58 private static final Logger logger = Logger.getLogger(HexView.class.getName());
59 private static final long serialVersionUID = 1L;
60 private final int bytesPerLine;
61 private final HexViewListener hexViewListener = new HexViewListener();
62 private final JTextComponent hexViewTextArea;
63 private final JTextComponent asciiViewTextArea;
64 private final JLabel statusLabel;
65 private final DefaultHighlighter.DefaultHighlightPainter highlighterPainter;
66 // these flags are used to ensure we don't end up in a circular event loop where
67 // one component fires an event on the other, who volley's it back.
68 private int hexLastSelectionStart = 0;
69 private int hexLastSelectionEnd = 0;
70 private int asciiLastSelectionStart = 0;
71 private int asciiLastSelectionEnd = 0;
72
78 HexView(ByteBuffer buf) {
79 this(buf, DEFAULT_BYTES_PER_LINE);
80 }
81
86 HexView(ByteBuffer buf, int bytesPerLine) {
87 super(new BorderLayout());
88 this.bytesPerLine = bytesPerLine;
89
90 Font font = new Font("Monospaced", Font.PLAIN, 12); //Non-NLS
91 //Font should be left alone as we want to ensure a monospaced font is used
92 //when displaying Hex, instead of the default font.
93
94 JTextComponent offsetView = new JTextArea();
95 this.hexViewTextArea = new JTextArea();
96 this.asciiViewTextArea = new JTextArea();
97 JPanel statusView = new JPanel();
98
99 // status bar
100 statusView.setBorder(new BevelBorder(BevelBorder.LOWERED));
101 this.add(statusView, BorderLayout.SOUTH);
102 statusView.setPreferredSize(new Dimension(this.getWidth(), 18));
103 statusView.setLayout(new BoxLayout(statusView, BoxLayout.X_AXIS));
104 this.statusLabel = new JLabel("");
105 this.statusLabel.setHorizontalAlignment(SwingConstants.LEFT);
106 statusView.add(this.statusLabel);
107
108 // right panes are split
109 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, this.hexViewTextArea, this.asciiViewTextArea);
110 splitPane.setResizeWeight(0.5);
111 splitPane.setOneTouchExpandable(true);
112 splitPane.setContinuousLayout(true);
113
114 // three panes sitting together
115 JPanel panes = new JPanel(new BorderLayout());
116 panes.add(offsetView, BorderLayout.WEST);
117 panes.add(splitPane, BorderLayout.CENTER);
118 JScrollPane scroller = new JScrollPane(panes);
119 this.add(scroller, BorderLayout.CENTER);
120
121 offsetView.setFont(font);
122 hexViewTextArea.setFont(font);
123 asciiViewTextArea.setFont(font);
124
125 StringBuilder offsetSB = new StringBuilder();
126 StringBuilder hexSB = new StringBuilder();
127 StringBuilder asciiSB = new StringBuilder();
128
129 buf.position(0x0);
130 for (int i = 0; i < buf.limit(); i++) {
131 if (i % this.bytesPerLine == 0x0) {
132 offsetSB.append(String.format("0x%x \n", i));
133 }
134
135 byte b = buf.get();
136 char[] hex = new char[CHAR_ARRAY_SIZE];
137 hex[0] = HEX_DIGITS[(b >>> 4) & 0x0F];
138 hex[1] = HEX_DIGITS[b & 0x0F];
139 hex[2] = ' ';
140 hexSB.append(hex);
141
142 if (b >= ' ' && b <= '~') {
143 asciiSB.append((char) b);
144 } else {
145 asciiSB.append('.');
146 }
147
148 if (i % this.bytesPerLine == this.bytesPerLine - 1) {
149 hexSB.append('\n');
150 asciiSB.append('\n');
151 }
152 }
153
154 offsetView.setText(offsetSB.toString());
155 this.hexViewTextArea.setText(hexSB.toString());
156 this.asciiViewTextArea.setText(asciiSB.toString());
157 this.hexViewTextArea.addCaretListener(hexViewListener);
158 this.asciiViewTextArea.addCaretListener(hexViewListener);
159 this.asciiViewTextArea.setSelectedTextColor(this.asciiViewTextArea.getForeground());
160 this.hexViewTextArea.setSelectedTextColor(this.asciiViewTextArea.getForeground());
161 this.highlighterPainter = new DefaultHighlighter.DefaultHighlightPainter(this.hexViewTextArea.getSelectionColor());
162 }
163
168 private class HexViewListener implements CaretListener {
169
170 @Override
171 public void caretUpdate(CaretEvent e) {
172 if (e.getMark() == e.getDot()) {
173 this.clearHighlight();
174 }
175
176 if (e.getSource() == asciiViewTextArea) {
177 int startByte = e.getMark();
178 int endByte = e.getDot();
179
180 if (startByte > endByte) {
181 int t = endByte;
182 endByte = startByte;
183 startByte = t;
184 }
185
186 // the number of line endings before the start,end points
187 int startRows = (startByte - (startByte % bytesPerLine)) / bytesPerLine;
188 int endRows = (endByte - (endByte % bytesPerLine)) / bytesPerLine;
189
190 // the byte index of the start,end points in the ASCII view
191 startByte -= startRows;
192 endByte -= endRows;
193
194 // avoid the loop
195 if (asciiLastSelectionStart == startByte && asciiLastSelectionEnd == endByte) {
196 return;
197 }
198 asciiLastSelectionStart = startByte;
199 asciiLastSelectionEnd = endByte;
200
201 this.setSelection(startByte, endByte);
202 } else if (e.getSource() == hexViewTextArea) {
203 int startByte = e.getMark();
204 int endByte = e.getDot();
205
206 if (startByte > endByte) {
207 int t = endByte;
208 endByte = startByte;
209 startByte = t;
210 }
211
212 // the number of line endings before the start,end points
213 int startRows = (startByte - (startByte % bytesPerLine)) / (CHAR_ARRAY_SIZE * bytesPerLine);
214 int endRows = (endByte - (endByte % bytesPerLine)) / (CHAR_ARRAY_SIZE * bytesPerLine);
215
216 // the byte index of the start,end points in the ASCII view
217 startByte -= startRows;
218 startByte /= CHAR_ARRAY_SIZE;
219 endByte -= endRows;
220 endByte /= CHAR_ARRAY_SIZE;
221
222 if (hexLastSelectionStart == startByte && hexLastSelectionEnd == endByte) {
223 return;
224 }
225 hexLastSelectionStart = startByte;
226 hexLastSelectionEnd = endByte;
227
228 this.setSelection(startByte, endByte);
229 } else {
230 logger.log(Level.INFO, "Source of event was neither the ascii view or the hex view text area");
231 }
232 }
233
242 @Messages({"# {0} - startByteD",
243 "# {1} - endByteD",
244 "# {2} - lengthD",
245 "# {3} - startByteH",
246 "# {4} - endByteH",
247 "# {5} - lengthH",
248 "HexView.statusTemplate.nonZeroLength=Selection: {0} to {1} (len: {2}) [{3} to {4} (len: {5})",
249 "# {0} - startByteDec",
250 "# {1} - startByteHex",
251 "HexView.statusTemplate.zeroLength=Position: {0} [{1}])"})
252 private void setSelection(int startByte, int endByte) {
253 this.setHighlight(startByte, endByte);
254
255 if (startByte != endByte) {
256 /*
257 * param 1 Start
258 * param 2 End
259 * param 3 Len
260 */
261 int length = endByte - startByte;
262 String text = Bundle.HexView_statusTemplate_nonZeroLength(
263 startByte,
264 endByte,
265 length,
266 String.format("0x%1$x", startByte),
267 String.format("0x%1$x", endByte),
268 String.format("0x%1$x", length));
269 statusLabel.setText(text);
270 } else {
271 /*
272 * param 1 Start
273 */
274 String text = Bundle.HexView_statusTemplate_zeroLength(startByte, String.format("0x%1$x", startByte));
275 statusLabel.setText(text);
276 }
277 }
278
282 private void clearHighlight() {
283 asciiViewTextArea.getHighlighter().removeAllHighlights();
284 hexViewTextArea.getHighlighter().removeAllHighlights();
285 }
286
293 private void setHighlight(int startByte, int endByte) {
294 int startRows = (startByte - (startByte % bytesPerLine)) / bytesPerLine;
295 int endRows = (endByte - (endByte % bytesPerLine)) / bytesPerLine;
296
297 this.clearHighlight();
298
299 try {
300 asciiViewTextArea.getHighlighter().addHighlight(startByte + startRows, endByte + endRows, highlighterPainter);
301 hexViewTextArea.getHighlighter().addHighlight((startByte * CHAR_ARRAY_SIZE) + startRows, (endByte * CHAR_ARRAY_SIZE) + endRows, highlighterPainter);
302 } catch (BadLocationException ex) {
303 logger.log(Level.WARNING, "Invalid highlighting location specified", ex);
304 }
305 }
306 }
307}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
void setSelection(int startByte, int endByte)
Definition HexView.java:252
void setHighlight(int startByte, int endByte)
Definition HexView.java:293

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.