フィード(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();
}
}
}
}