DocumentTreeViewer.java
import java.io.*;
import java.net.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.w3c.dom.*;

// XML DOM Tree を表示するプログラム
// 使い方: java DocumentTreeViewer url [encoding]
//         (encoding は指定しないと utf-8)

public class DocumentTreeViewer {
    public static void main(String[] args) {
        String encoding = "utf-8";  // 文字コードの初期値は utf-8
        if(args.length > 1)         // 第2引数があったらそれを文字コードとして指定
            encoding = args[1];
        // Document Tree を生成 (URL, 文字コードを指定)
        DocumentTree tree = new DocumentTree(args[0], encoding);
        // Tree を表示
        tree.show();
    }
}

// XML DOM Tree を表すクラス
class DocumentTree {
    private Document document;
    private int depth; // 階層
    public DocumentTree(String urlString, String encoding) {
        document = null;
        try {
            // まずは接続し、データを読む Reader を生成
            URL url = new URL(urlString);
            URLConnection connection = url.openConnection();
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            InputStreamReader reader = new InputStreamReader(inputStream,
                                                             encoding);
            BufferedReader in = new BufferedReader(reader);
            // Reader を使ってストリームを読み、それを解析して Document を生成
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(new InputSource(in));
            in.close();
        }
        catch (ParserConfigurationException e) {
            System.err.println("DocumentBuilderFactory生成エラー:" + e);
        }
        catch (SAXException e) {
            System.err.println("構文エラー:" + e);
        }
        catch (IOException e) {
            System.err.println("入出力エラー:" + e);
        }
    }
    /** DOM Tree の内容を表示 */
    public void show() {
        depth = 0;
        try {
            // root 要素を与えて showTree を呼び出す
            showTree(document.getDocumentElement());
        }
        catch (DOMException e) {
            System.err.println("DOMエラー:" + e);
        }
    }
    /** 引数 node 以下の tree を表示 */
    private void showTree(Node node) {
        for(Node current = node.getFirstChild();
                 current != null;
                 current = current.getNextSibling()) {
            if(current.getNodeType() == Node.ELEMENT_NODE) { // ノードは要素?
                String nodeName = current.getNodeName();
                // 中括弧 { } を使って階層の深さを表現
                System.out.println(indent() + nodeName + " {");
                depth++;
                showTree(current); // さらに子ノードを見る (再帰)
                depth--;
                System.out.println(indent() + "}");
            }
            else if(current.getNodeType() == Node.TEXT_NODE // ノードはテキスト?
                    && current.getNodeValue().trim().length() != 0)
                System.out.println(indent() + current.getNodeValue());
            else if(current.getNodeType() == Node.CDATA_SECTION_NODE) // ノードはCDATA?
                System.out.println(indent() + current.getNodeValue());
                                                         // HTMLタグなどを含む
                ; // 上記以外のノードでは何もしない
        }
    }
    // 階層 depth にあわせた字下げ用の空白を生成
    private String indent() {
        String indent = "";
        for(int i = 0; i < depth; i++)
            indent += "  ";
        return indent;
    }
}