2010年2月27日土曜日

[java][swing][borderlayout]リサイズにコンポーネントを追従させる

お疲れ様です。
JDialogをリサイズさせたときに、JTextFieldのサイズも一緒にリサイズされたらいいのにと。
下図のjLabel1とjTextField1は、JPanelに属させていて、そのレイアウトをFlowLayoutに
した例です。これだとリサイズに追従しません。
FlowLayoutをやめてBorderLayoutに変更します。ラベルをBorderLayout.LINE_STARTに配して、テキストフィールドをBorderLayout.CENTERに配します。こうなります。
テキストフィールドがダイアログの幅(というかJPanelの幅)いっぱいに広がりました。
これを幅と高さ共にリサイズしてみます。
リサイズに追従しました。思いがけず高さまで追従しましたが、これはこれで意味がありますまずいので、後日ちゃんとします。


ではまた。

[java][swing][jtable]テーブルのヘッダのラベルを左寄りにする

お疲れ様です。
NetBeansのGUIビルダでJTableを作るとノンコーディングでこんな感じになります。




テーブルのヘッダのラベルがセンタリングされているのがわかりますね。
これを左寄りにするにはこうします。
---
DefaultTableCellRenderer dtcr = (DefaultTableCellHeaderRenderer)jTable1.getTableHeader().getDefaultRenderer();
dtcr.setHorizontalAlignment(SwingConstants.LEFT);
---
GUIビルダで生成されるinitComponents()よりも後ろで効かせればいいです。



はい。左に詰まりました。

ではまた。

2010年2月21日日曜日

[java][io]RandomAccessFileのreadLineをUTF-8に対応させる

オリジナルのreadLineは1バイト読み込んで1つのcharに変換し、
最後にStringとして返しますが、そうすると日本語とか復元できません。
これはAPIにも書いてあります。
そこでreadLineのコードをちょこっと改造してみました。
---
/**
 * <code>RandomAccessFile.read</code>で読み込んだバイト配列を
 * UTF-8エンコードした<code>String</code>で返します。
 * EOFに達するとnullを返します。
 * @param f
 * @return
 * @throws IOException
 */
public static String readLineRandomAccessFileUTF(final RandomAccessFile f) 
  throws IOException {
  List<Byte> input = new ArrayList<Byte>();
  int c = -1;
  boolean eol = false;
  while (!eol) {
    switch (c = f.read()) {
    case -1: //EOFに達した場合
    case '\n':
      eol = true;
      break;
    case '\r':
      eol = true;
      long cur = f.getFilePointer();
      if ((f.read()) != '\n') {
        f.seek(cur);
      }
      break;
    default:
      input.add((byte)c);
      break;
    }
  }


  if ((c == -1) && (input.size() == 0)) {
    return null;
  }
  byte[] bytes = new byte[input.size()];
  for(int i=0;i<input.size();i++){
    bytes[i]=input.get(i);
  }
  return new String(bytes, "utf8");
}
---
今回はテストケース無しです。

2010年2月14日日曜日

[java][dom]ネストした要素をフラットに再配置する

例えばfontタグの中にfontタグを入れないように一個ずつ閉じましょうみたいなことです。
次の実装の例は親子関係をフラットに再配置します。ただし、矯正できるのは親子関係(二代)までです。


---
/**
 * ネストした要素をフラットに再配置します。
 * 
 * 変換前
 * <phrase>We <phrase>are </phrase>the <phrase>world.</phrase></phrase>;
 * 変換後
 * <phrase>We </phrase><phrase>are </phrase><phrase>the </phrase><phrase>world.</phrase>
 * @param target
 */
public static void flattenNestedElement(final Element target){
  if(target == null) throw new IllegalArgumentException();
  
  String targetName = target.getNodeName();
  Element parent = (Element)target.getParentNode();
  for(int i=0;i<target.getChildNodes().getLength();){
    Node child = target.getChildNodes().item(i);
    if(child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(targetName)){
      parent.insertBefore(child, target);
    }else{
      Node copy = target.cloneNode(false);
      parent.insertBefore(copy, target);
      copy.appendChild(child);
    }
  }
  parent.removeChild(target);
}
---

テストケースです。
---
@Test
public void testFlattenNestedElement_DocBook4_Phrase() throws Exception{
  StringBuilder sb = new StringBuilder();
  sb.append("<section><para>");
  sb.append("<phrase revisionflag=\"changed\">基本 XML 値を");
  sb.append("<phrase revision=\"01\"><emphasis role=\"bold\">定数</emphasis>として</phrase>");
  sb.append("格納する");
  sb.append("<phrase revision=\"02\">ユーティリティー</phrase>");
  sb.append("クラスです。</phrase>");
  sb.append("</para></section>");
  String xml = sb.toString();
  
  sb.setLength(0);
  sb.append("<section><para>");
  sb.append("<phrase revisionflag=\"changed\">基本 XML 値を</phrase>");
  sb.append("<phrase revision=\"01\"><emphasis role=\"bold\">定数</emphasis>として</phrase>");
  sb.append("<phrase revisionflag=\"changed\">格納する</phrase>");
  sb.append("<phrase revision=\"02\">ユーティリティー</phrase>");
  sb.append("<phrase revisionflag=\"changed\">クラスです。</phrase>");
  sb.append("</para></section>");
  String expected = sb.toString();
  
  Document doc = DomUtils.stringToDocument(xml);
  NodeList nodeList = DomUtils.findNodesByXPath(doc, "/section/para/phrase");
  Element target = (Element)nodeList.item(0);
  DomUtils.flattenNestedElement(target);
  String actual = DomUtils.nodeToString(doc);
  actual = XMLUtils.removeXMLDeclaration(actual);
  assertEquals(expected, actual);
}
---

ではまた。

2010年2月13日土曜日

[java][dom]構造化されたタグの解除(2)

もう少し多層なテストケースです。
---
/**
 * 子要素を親要素に移動して自身の要素を削除することによりタグを解除できるか。
 * <p>
 * DocBook4のvariablelistの場合
 */
@Test
public void testPullUpChildByXPath_DocBook4_Variablelist() throws Exception{
  StringBuilder sb = new StringBuilder();
  sb.append("<section>");
  sb.append("<title>Java Platform, Standard Edition 6 API 仕様</title>");
  sb.append("<variablelist>");
  sb.append("<varlistentry>");
  sb.append("<term revisionflag=\"changed\">java.applet</term>");
  sb.append("<listitem>");
  sb.append("<para>アプレットの作成、およびアプレットとアプレットコンテキストとの通信に使用するクラスの作成に必要なクラスを提供します。</para>");
  sb.append("</listitem>");
  sb.append("</varlistentry>");
  sb.append("<varlistentry>");
  sb.append("<term>java.<phrase revisionflag=\"changed\">awt</phrase></term>");
  sb.append("<listitem>");
  sb.append("<para>ユーザーインタフェースの作成およびグラフィックスとイメージのペイント用のすべてのクラスを含みます。</para>");
  sb.append("</listitem>");
  sb.append("</varlistentry>");
  sb.append("<varlistentry>");
  sb.append("<term>java.beans</term>");
  sb.append("<listitem>");
  sb.append("<para>カラースペースのクラスを</para>");
  sb.append("<para>提供します。</para>");
  sb.append("</listitem>");
  sb.append("</varlistentry>");
  sb.append("</variablelist>");
  sb.append("</section>");
  String xml = sb.toString();
  
  sb.setLength(0);
  sb.append("<section>");
  sb.append("<title>Java Platform, Standard Edition 6 API 仕様</title>");
  sb.append("java.applet");
  sb.append("<para>アプレットの作成、およびアプレットとアプレットコンテキストとの通信に使用するクラスの作成に必要なクラスを提供します。</para>");
  sb.append("java.<phrase revisionflag=\"changed\">awt</phrase>");
  sb.append("<para>ユーザーインタフェースの作成およびグラフィックスとイメージのペイント用のすべてのクラスを含みます。</para>");
  sb.append("java.beans");
  sb.append("<para>カラースペースのクラスを</para>");
  sb.append("<para>提供します。</para>");
  sb.append("</section>");
  String expected = sb.toString();
  
  Document doc = DomUtils.stringToDocument(xml);
  //子の引き上げは下層からやります。
  DomUtils.pullUpChildByXPath(doc, "/section/variablelist/varlistentry/term");
  DomUtils.pullUpChildByXPath(doc, "/section/variablelist/varlistentry/listitem");
  DomUtils.pullUpChildByXPath(doc, "/section/variablelist/varlistentry");
  DomUtils.pullUpChildByXPath(doc, "/section/variablelist");
  String actual = DomUtils.nodeToString(doc);
  actual = XMLUtils.removeXMLDeclaration(actual);
  assertEquals(expected, actual);
}
---

ではまた。

[java][dom]構造化されたタグの解除

こんな感じで。
---
/**
 * 子要素を親要素に移動して自身の要素を削除することによりタグを解除します。
 * @param doc
 * @param xpathExpression 削除したい要素を指定するためのXPath式
 */
public static void pullUpChildByXPath(final Document doc, final String xpathExpression) 
  throws XPathExpressionException{
  XPathFactory factory = XPathFactory.newInstance();
  XPath xpath = factory.newXPath();
  NodeList nodes = 
    (NodeList) xpath.evaluate(xpathExpression, doc, XPathConstants.NODESET);
  for(int i=0;i<nodes.getLength();i++){
    Node node = nodes.item(i);
    if(node.getNodeType() == Node.ELEMENT_NODE){
      pullUpChild((Element)node);
    }
  }
}
/**
 * 子要素を親要素に移動して自身の要素を削除します。
 * @param element 削除したい要素
 */
public static void pullUpChild(final Element element){
  //親を呼び出します。
  Node parent = element.getParentNode();
  NodeList childNodes = element.getChildNodes();  
  //ノードの挿入は親に頼みます。
  for(int j=0; j < childNodes.getLength(); j++){
    Node child = childNodes.item(j).cloneNode(true);
    parent.insertBefore(child, element);
  }
  parent.removeChild(element);
}
---

テストケースです。
---
@Test
public void testPullUpChildByXPath_DocBook4_OrderedList() throws Exception{
  StringBuilder sb = new StringBuilder();
  sb.append("<section>");
  sb.append("<title>Java Platform, Standard Edition 6 API</title>");
  sb.append("<orderedlist>");
  sb.append("<title>パッケージ</title>");
  sb.append("<listitem>");
  sb.append("<para revisionflag=\"added\">java.applet</para>");
  sb.append("</listitem>");
  sb.append("<listitem>");
  sb.append("<para>java.<phrase revisionflag=\"changed\">awt</phrase></para>");
  sb.append("</listitem>");
  sb.append("<listitem>");
  sb.append("<para>java.beans</para>");
  sb.append("<para>Beans (JavaBeans アーキテクチャーに基づいたコンポーネント) の開発に関連するクラスが含まれています。</para>");
  sb.append("</listitem>");
  sb.append("</orderedlist>");
  sb.append("</section>");
  String xml = sb.toString();
  
  sb.setLength(0);
  sb.append("<section>");
  sb.append("<title>Java Platform, Standard Edition 6 API</title>");
  sb.append("<title>パッケージ</title>");
  sb.append("<para revisionflag=\"added\">java.applet</para>");
  sb.append("<para>java.<phrase revisionflag=\"changed\">awt</phrase></para>");
  sb.append("<para>java.beans</para>");
  sb.append("<para>Beans (JavaBeans アーキテクチャーに基づいたコンポーネント) の開発に関連するクラスが含まれています。</para>");
  sb.append("</section>");
  String expected = sb.toString();
  
  Document doc = DomUtils.stringToDocument(xml);
  //子の引き上げは下層からやります。
  DomUtils.pullUpChildByXPath(doc, "/section/orderedlist/listitem");
  DomUtils.pullUpChildByXPath(doc, "/section/orderedlist");
  String actual = DomUtils.nodeToString(doc);
  actual = XMLUtils.removeXMLDeclaration(actual);
  assertEquals(expected, actual);
}
---

ではまた。

2010年2月11日木曜日

[java][dom]子要素を残して自身を削除する

なんか、悲しいタイトルになってしまった。
---
/**
 * <code>org.w3c.dom</code>の子要素を残して自身を削除できるか。
 * @throws Exception
 */
@Test
public void testNonMethod_InsertBeforeChildNodes() throws Exception{
  String xml = "<html><head/><body><h1>クラス Pattern</h1><span class=\"normal\">コンパイル済みの正規表現です。</span>" + 
  "<h1>クラス Matcher</h1><span class=\"normal\">マッチ操作を行うエンジンです。</span></body></html>";
  Document doc = DomUtils.stringToDocument(xml);
  //span要素を削除して、span要素の子孫は残します。
  NodeList nodeList = DomUtils.findNodesByXPath(doc, "//*");
  for(int i=0;i<nodeList.getLength();i++){
    Node node = nodeList.item(i);
    if(node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("span")){
      //親を呼び出します。
      Node parent = node.getParentNode();
      NodeList childNodes = node.getChildNodes();  
      //ノードの挿入は親に頼みます。
      for(int j=0; j < childNodes.getLength(); j++){
        Node child = childNodes.item(j).cloneNode(true); //修正
        parent.insertBefore(child, node);
      }
      parent.removeChild(node);
    }
  }
  String actual = DomUtils.nodeToString(doc);
  System.out.println(actual);
}
---

出力結果
---
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   </head>
   <body>
      <h1>クラス Pattern</h1>コンパイル済みの正規表現です。
      <h1>クラス Matcher</h1>マッチ操作を行うエンジンです。
   </body>
</html>
---

ではまた。


修正:
誤)Node child = childNodes.item(j);
正)Node child = childNodes.item(j).cloneNode(true);

[java][swing][jdialog]Escapeキーで閉じる

JDialogの初期処理のどこかでこれをやります。
---
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
rootPane.registerKeyboardAction(new ActionListener(){
    public void actionPerformed(ActionEvent e) {
      setVisible(false);
    }
  }, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
---


ではまた。

[java][dom]テキストノードをタグでラップする

DOM APIの使用例です。
---
/**
 * <code>org.w3c.dom</code>のテキストノードを、新しい要素の子に移動できるか。
 * @throws Exception
 */
@Test
public void testNonMethod_MoveTextNodeToNewElement() throws Exception{
  String xml = "<html><head/><body><h1>クラス Pattern</h1>コンパイル済みの正規表現です。" + 
  "<h1>クラス Matcher</h1>マッチ操作を行うエンジンです。</body></html>";
  Document doc = DomUtils.stringToDocument(xml);
  //bodyの子要素のテキストノードを<font color="red"></font>要素で囲みます。
  NodeList nodeList = DomUtils.findNodesByXPath(doc, "/html/body/node()");
  for(int i=0;i<nodeList.getLength();i++){
    Node node = nodeList.item(i);
    if(node.getNodeType() == Node.TEXT_NODE){
      //親を呼び出します。
      Node parent = node.getParentNode();
      //新しい要素を生成します。
      Element newElement = doc.createElement("font");
      newElement.setAttribute("color", "red");
      //ノードの挿入は親に頼みます。
      parent.insertBefore(newElement, node);
      //テキストノードを、挿入された新しい要素の子に移動します。元の場所から削除する必要はありません。
      newElement.appendChild(node);
    }
  }
  String actual = DomUtils.nodeToString(doc);
  System.out.println(actual);
}
---
DomUtilsクラスは自作のクラスです。メソッド名から実装を想像してください。^ ^;


出力結果
---
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
   </head>
   <body>
      <h1>クラス Pattern</h1><font color="red">コンパイル済みの正規表現です。</font><h1>クラス Matcher</h1><font color="red">マッチ操作を行うエンジンです。</font></body>
</html>
---
ではまた。

2010年2月9日火曜日

[java][dom]StringをDOMドキュメントに変換する

はい。変換しますよっと。
---
/**
* XMLソースから<code>org.w3c.dom.Document</code>を返します。
* @param xml
* @return
* @throws Exception パースに失敗した場合
*/
public static org.w3c.dom.Document stringToDocument(final String xml) throws Exception{
Document doc = null;
try {
InputSource is = new InputSource(new StringReader(xml));
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setXIncludeAware(true);
factory.setNamespaceAware(true);
DocumentBuilder docBuilder = factory.newDocumentBuilder();
doc = docBuilder.parse(is);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
return doc;
}
---


そしてテストケースです。
---
/**
* XMLソースから<code>org.w3c.dom.Document</code>を返せるか
* @throws Exception
*/
@Test 
public void testStringToDocument() throws Exception{
String xml = "<html><head><title>Title</title></head><body><h1>Top</h1></body></html>";
Document doc = DomUtils.stringToDocument(xml);
assertEquals("html", doc.getFirstChild().getNodeName());
assertEquals("head", doc.getFirstChild().getFirstChild().getNodeName());
assertEquals("title", doc.getFirstChild().getFirstChild().getFirstChild().getNodeName());
assertEquals("Title", doc.getFirstChild().getFirstChild().getFirstChild().getTextContent());
}
---


ではまた。