熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Javascript >> 正文

用Java制作十六進制編輯器

2013-11-23 17:56:13  來源: Javascript 

  本人在用Java編制一個網絡應用測試工具的時候迫切希望能以十六進制方式顯示和編輯socket上傳輸的數據於是自己動手寫了一個編輯器類實現基本的十六進制編輯功能效果如圖一圖二所示可以看到編 
  輯器可以支持常規方式和十六進制兩種方式對數據進行編輯
  分析對於同一段數據值用兩種方式來顯示和編輯則用MVC(模型-視圖-控制器)模式來作為主結構是再合適不過的了模型的作用是保存真實的數據值同時提供若干提取和修改數據的方法視圖是數據在用戶界面上的表示控制器定義用戶界面對用戶輸入的響應方式即把用戶的鍵盤動作和鼠標動作解釋成模型中的數據操作方法出於簡化的考慮本例中把視圖和控制器合並到了一起
  模型的設計編輯器必須能處理任意字節塊所以考慮模型內用字節數組來存儲數據要提供在指定偏移處增加修改和刪除字節塊的操作當模型內的數據被改變時要及時通知視圖來刷新用戶界面或其它感興趣的對象
   
  圖一
   
  圖二
  
  視圖和控制器的設計
  
  對於常規編輯的視圖只需把模型中的字節數組轉化成String使用一個文本區域組件JTextArea來顯示即可JTextArea本身也是一個遵循MVC模式的Swing組件它的控制器即可被用來作為我們自己的控制器監聽它的文本增加修改和刪除事件從而控制我們自己的數據模型
  
  對於進制編輯的視圖同樣可以用JTextArea來顯示只是在顯示之前要對模型中的數據進行若干加工如每行顯示個字節每行都要加表示偏移量的頭行尾要加這一行數據的字符串表示形式它的控制器則不能簡單的利用JTextArea的控制器了為了保證顯示格式不被打亂需要監視它的所有光標移動事件鍵盤擊鍵事件等同時為了保持與UltraEdit的十六進制編輯器的功能一致性對它的數據的增加刪除功能提供兩個按鈕詢問用戶操作的字節數如果增加n個字節則在輸入光標處插入n個十六進制值為的字符(字符空格)
  十六進制編輯器的主要結構請參見下圖由於篇幅關系圖中只列出了十六進制編輯部分常規編輯部分請讀者自行設計
  
  圖三
  
  下面分別就幾個主要的方法的功能和主要流程加以說明
  
  HexPanedisplayValue方法它主要完成數據的顯示工作
  
  public void diplayValue() {
  
  canvassetText();
  
  
  byte[] data = modelgetData();
  
  int dataLen = datalength;
  
  
  // 把字節數組按每個字節為一塊進行分塊
  
  int lines = dataLen / + ;
  
  int tails = dataLen % ;
  
  int offset = ;
  
  for(int i = ; i < lines; 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
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.