FeedSearcher.java

フィード(RSS)を検索(フィルタリング)するプログラムです。 DOMを利用しています。

指定された複数の文字列(検索語1, 検索語2, 検索語3, ...)のいずれも、title 要素か description 要素のどちらかに出現している item 要素を探し、その item 要素の内容(title, link, descriptionなど)を表示するプログラムです。

FeedViewer.java をベースにしているので、getElementsByTagName や XPath は利用していません。

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

// Feed の内容を検索するプログラム (RSS 1.0/2.0対応)
// FeedViewer.java ベースなので XPath 等は使用していない
// 使い方: java FeedSearcher url word1 [word2 word3 ...]
//         (encoding は utf-8 固定)

class Feed {
    private URL url;
    private String encoding;
    private Document document;
    private ArrayList<Item> itemList;
    public Feed() {
        url = null;
        encoding = "utf-8";
        document = null;
        itemList = new ArrayList<Item>();
    }
    public void setURL(String url) {
        try {
            this.url = new URL(url);
        }
        catch(IOException e) {
            System.err.println("間違ったURL: " + url);
        }
    }
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }
    /** URLで指示されたフィードを取得し DOM tree を構築 */
    public void connect() {
        BufferedReader in = null;
        try {
            URLConnection connection = url.openConnection();
            connection.connect();
            InputStream inputStream = connection.getInputStream();
            InputStreamReader reader = new InputStreamReader(inputStream,
                                                             encoding);
            in = new BufferedReader(reader);
        }
        catch (IOException e) {
            System.err.println("接続エラー: " + e);
            System.exit(1);
        }

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            document = builder.parse(new InputSource(in));
        }
        catch (ParserConfigurationException e) {
            System.err.println("DocumentBuilderFactory生成エラー:" + e);
        }
        catch (SAXException e) {
            System.err.println("構文エラー:" + e);
        }
        catch (IOException e) {
            System.err.println("入出力エラー:" + e);
        }

        try {
            in.close();
        }
        catch (IOException e) {
            System.err.println("接続終了エラー: " + e);
        }
    }
    /** Feed の内容を Item オブジェクトのリストとして返す */
    public ArrayList<Item> getItemList() {
        try {
            // root要素 (rdf:RDF or rss) を得る
            Element rootElement = document.getDocumentElement();
            String rootElementName = rootElement.getNodeName();
            System.out.println("root element name: " + rootElementName);
            findItems(rootElement);
        }
        catch (DOMException e) {
            System.err.println("DOMエラー:" + e);
        }
        return itemList;
    }
    /** node の下位(子孫)から item 要素を探し Item オブジェクトを得る */
    private void findItems(Node node) {
        for(Node current = node.getFirstChild();
                 current != null;
                 current = current.getNextSibling()) {
            if(current.getNodeType() == Node.ELEMENT_NODE) { // そもそも要素か
                String nodeName = current.getNodeName();
                if(nodeName.equals("item")) {
                    // System.out.println("item 要素を発見!");
                    // item 要素から Item オブジェクトを得る
                    Item item = getItem(current);
                    // Item オブジェクトをリストに追加
                    // (ここで条件を満たすかチェックする方法もあり)
                    itemList.add(item);
                }
                else {
                    System.out.println(nodeName + " 要素をスキップ");
                                        // channel, items などがスキップされる
                    findItems(current); // さらに子ノードを見る (再帰)
                }
            }
        }
    }
    /** item 要素から Item オブジェクトを生成 */
    private Item getItem(Node node) {
        String title = null;
        String link = null;
        String description = null;
        String date = null;
        String creator = null;
        for(Node current = node.getFirstChild();
                 current != null;
                 current = current.getNextSibling()) { // 子ノードを先頭から
            if(current.getNodeType() == Node.ELEMENT_NODE) { // 要素だったら
                String nodeName = current.getNodeName();
                if(nodeName.equals("title"))
                    title = getContent(current);
                else if(nodeName.equals("link"))
                    link = getContent(current);
                else if(nodeName.equals("description"))
                    description = getContent(current);
                else if(nodeName.equals("dc:date") || nodeName.equals("pubDate"))
                    date = getContent(current);
                else if(nodeName.equals("dc:creator"))
                    creator = getContent(current);
                else if(nodeName.equals("guid") ||
                        nodeName.equals("category") ||
                        nodeName.startsWith("dc:") ||
                        nodeName.startsWith("rdf:") ||
                        nodeName.startsWith("dcq:")){
                    ; // 処理しない
                }
                else {
                    ; // 処理しない
                }
            }
            // 要素 (Node.ELEMENT_NODE) でなかったら何もしない (改行など)
        }
        return new Item(title, link, description, date, creator);
    }
    /** title, link, description などの要素から内容を取り出す */
    private String getContent(Node node) {
        String content = "";
        node = node.getFirstChild(); // 子ノードがテキストのはず
        if(node.getNodeType() == Node.TEXT_NODE
             && node.getNodeValue().trim().length() != 0)
            content = node.getNodeValue();
        else if(node.getNodeType() == Node.CDATA_SECTION_NODE)
            content = node.getNodeValue();  // HTMLタグなどを含む
        return content;
    }
}

/** 1つの item 要素に対応するオブジェクトを表すクラス */
class Item {
    private String title;
    private String link;
    private String description;
    private String date;
    private String creator;
    public Item (String title, String link, String description,
                 String date, String creator) {
        this.title = title;
        this.link = link;
        this.description = description;
        this.date = date;
        this.creator = creator;
    }
    /** 単語群(配列の1番目から)をすべて含むかチェック */
    public boolean containsAll(String[] words) {
        boolean containsAllWords = true; // 単語すべてが出現しているか否か
                                 // 初期値を true にしておいて、
                                 // 出現していない単語が見つかったら false に
        for(int i = 1; i < words.length; i++) { // 各検索語iについて
            boolean appearing = false; // words[i] が出現しているか
            if(title.indexOf(words[i]) != -1) // title に出現?
               appearing = true;
            if(description.indexOf(words[i]) != -1) // description に出現?
               appearing = true;
            if(appearing == false) { // いずれの場所にも出現していない
                containsAllWords = false; // 出現していない単語があるよ
                break; // 1つでも出現していない単語が見つかったら中止
            }
        }
        return containsAllWords;
    }
    /** 各属性を文字列に変換(表示に利用) */
    public String toString() {
        String string = "";
        if(title != null)
            string += "title: " + title + "\n";
        if(link != null)
            string += "link: " + link + "\n";
        if(description != null)
            string += "description: " + description + "\n";
        if(date != null)
            string += "date: " + date + "\n";
        if(creator != null)
            string += "creator: " + creator + "\n";
        return string;
    }
}

class FeedSearcher {
    public static void main(String args[]) {
        Feed feed = new Feed();
        feed.setURL(args[0]);
        feed.setEncoding("utf-8"); // 文字コードは utf-8 に固定
        feed.connect();
        ArrayList<Item> itemList = feed.getItemList();
        // 表示
        Iterator<Item> iterator = itemList.iterator();
        while(iterator.hasNext()) {
            // いきなり表示するのではなく
            // System.out.print(iterator.next().toString());
            Item item = iterator.next();
            if(item.containsAll(args)) {
                System.out.print(item.toString());
                System.out.println();
            }
        }
    }
}