本人在用Java編制一個網絡應用測試工具的時候
輯器可以支持常規方式和十六進制兩種方式對數據進行編輯
分析
模型的設計
圖一
圖二
視圖和控制器的設計
對於常規編輯的視圖
對於
十六進制編輯器的主要結構請參見下圖
圖三
下面分別就幾個主要的方法的功能和主要流程加以說明
HexPane
public void diplayValue() {
canvas
byte[] data = model
int dataLen = data
// 把字節數組按每
int lines = dataLen /
int tails = dataLen %
int offset =
for(int i =
// 在canvas的新行上加上行標,如“000020:”,表示這是第3行;
canvas.append(lineHead(i));
canvas.append(" ");
// 把數據塊的字節值用Integer.toHexString()轉化成長度為48的字符串,數據塊不足16個字節的,在字符串後用空格補足;把字符串加入canvas的當前行;
for(offset = 0; offset < 16; offset ++) {
canvas.append((i < lines - 1 || offset < tails) ? byteHex(data[i * 16 + offset]) : " ");
canvas.append(" ");
}
canvas.append("| ");
// 把數據塊構造成字符串,添在canvas的行尾;
canvas.append(bytesToStr(data, i * 16, (i == lines - 1) ? tails : 16));
if(i < lines - 1) ta.append("\n");
};
}
這裡的bytesToStr方法有兩點特別需要注意的地方,一是不可見字符,如果不屏蔽這些字符,則我們的編輯器的顯示格式會被搞得亂七八糟,一般可以把ASCII值0到0x1F和0x7F的33個字符全部替換成0x2E,即字符小數點。tW.WingWit.cOM二是中文字符,因為每個中文字符是2個字節,如果數據塊的起始字節一個中文字符的一半(可以用ASCII值大於0x7F來判斷)的時候,將會顯示一串亂字符,處理方法是不顯示該字節。
為敘述方便,我們把canvas中顯示每一行的行標的區域稱為標號區,它寬度固定為8個字符(6個字符顯示標號,一個冒號和一個空格);把canvas中顯示十六進制數據的區域稱為數據區,寬度固定為48個字符(每字節用十六進制顯示為2字符寬,兩兩之間有一個空格,則總寬為16×3);把canvas中每行以字符串形式顯示數據的區域稱為字串值區,寬度不定(最短為8個字符――全中文狀態,最長為16個字符――全英文狀態)。
我們的canvas是一個Swing的文本組件,我們不但用它顯示數據,還顯示標號和字串值,而只有數據才是允許被編輯的,所以我們給canvas增加了CaretListener和KeyListener,當輸入光標落在不允許編輯的區域時,我們要把光標自動移到最近的允許編輯的地方去。
public void caretUpdate(CaretEvent e) { // 這個方法在輸入光標移動時被觸發
int pos = canvas.getCaretPosition(); // 輸入光標相對canvas第0行第0個字符的偏移量
int line = 0;
int startPos = 0;
try {
line = canvas.getLineOfOffset(pos); // 輸入光標位於第幾行
startPos = canvas.getLineStartOffset(line); // 當前行的第0個字符相對canvas第0行第0個字符的偏移量
}catch(BadLocationException exception) { }
if(pos - startPos < 8) // 輸入光標在標號區
canvas.setCaretPosition(startPos + 8); // 移動到數據區第0個字符
else if(pos - startPos > 54) // 輸入光標在字串值區
canvas.setCaretPosition(startPos + 54); // 移動到數據區最後一個字節
else if((pos - startPos - 8) % 3 == 2) { // 在數據區的間隙空格上
canvas.setCaretPosition(pos - 1); // 往前移一個字符
}
}
public void keyPressed(KeyEvent e) { // 當鍵盤被按下時觸發
int key = e.getKeyCode();
switch(key) { // 如果是方向鍵則移動輸入光標
case KeyEvent.VK_LEFT:
setCaretPrev();
break;
case KeyEvent.VK_RIGHT:
setCaretNext();
break;
case KeyEvent.VK_UP:
setCaretPrevLine();
break;
case KeyEvent.VK_DOWN:
setCaretNextLine();
break;
default:
return;
}
}
public void keyTyped(KeyEvent e) { // 在鍵盤的可見字符被輸入時觸發
char ch = e.getKeyChar();
if((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')
|| (ch >= 'A' && ch <= 'F')) {
int pos = canvas.getCaretPosition(); // 先獲取光標位置的信息
int line = 0, startPos = 0;
try {
line = canvas.getLineOfOffset(pos);
startPos = canvas.getLineStartOffset(line);
}catch(BadLocationException exception) { }
char c = (char)0;
if((pos - startPos - 8) % 3 == 0) { // 一個字節值的前4位
c = canvas.getText(pos + 1, 1).charAt(0);
model.updateBytes(line * 16 + (pos - startPos - 8) / 3, Byte.parseByte("" + (char)((ch << 4) + c), 16));
}else{ // 一個字節值的後4位
c = canvas.getText(pos - 1, 1).charAt(0);
model.updateBytes(line * 16 + (pos - startPos - 8) / 3, Byte.parseByte("" + (char)((c << 4) + ch), 16));
setCaretNext();
}
}
}
到這裡為止,十六進制編輯的顯示和輸入控制已經基本完成了,下面開始解決數據Model的問題。Model是用來保存數據的,並且提供增加、修改和刪除數據的方法,還要維護一個監聽者組,在數據被改變時向監聽者發出通知。這裡提供一個簡單的實現版本。
import javax.swing.event.EventListenerList;
public class DefaultBytesModel implements BytesModel{
private EventListenerList listeners = new EventListenerList(); // 監聽者組
private byte[] data = null;
public DefaultBytesModel (byte[] bytes) {
data = new byte[bytes.length];
}
public void addModelListener(BytesModelListener listener) {
listeners.add(BytesModelListener.class, listener);
}
public void removeModelListener(BytesModelListener listener) {
listeners.remove(BytesModelListener.class, listener);
}
/**
From:http://tw.wingwit.com/Article/program/Java/Javascript/201311/25384.html