//////////////////// Main.java ////////////////////
import java.io.*;
class Main
{
public static void main(String args[])
{
StringBuffer str_buf = new StringBuffer();
String buf;
try
{
BufferedReader reader = new BufferedReader(new FileReader(args[0]));
while((buf = reader.readLine()) != null)
{
str_buf.append(buf);
}
}
catch(IOException io_ex)
{
io_ex.printStackTrace();
}
TagChecker checker = new TagChecker(str_buf.toString());
UnclosedTagInfo info[] = checker.analyze();
for(int i=0; i<info.length; i++)
{
System.out.println(info[i].toString());
}
}
}
////////////////////////////////////////////////////////////
//////////////////// TagChecker.java ////////////////////
import java.io.*;
import java.util.Stack;
import java.util.Vector;
class TagChecker
{
// 許容タグリストファイル名
final String tag_list_file = "checklist.txt";
String text; // チェックすべきテキスト
private Vector list; // 閉じ忘れタグリスト
private Vector check_tag_list; // チェックタグリスト
private Stack stack; // タグチェック用スタック
// コンストラクタ
public TagChecker(String text)
{
this.text = text;
list = new Vector();
check_tag_list = new Vector();
stack = new Stack();
// 許容タグリストの取得
try
{
// 入力ストリームの作成
FileInputStream file_in = new FileInputStream(tag_list_file);
BufferedReader reader = new BufferedReader(new InputStreamReader(file_in));
// ファイルの読み込み
String buffer;
while((buffer = reader.readLine()) != null)
{
check_tag_list.add(buffer);
}
// ストリームを閉じる
reader.close();
file_in.close();
}
catch(IOException ioe)
{
System.err.println(ioe);
}
}
// タグの閉じ忘れチェックルーチン
public UnclosedTagInfo[] analyze()
{
try
{
// 入力ストリームの作成
StringReader string_reader = new StringReader(text);
BufferedReader reader = new BufferedReader(string_reader);
String buffer;
int current_position = 0; // 始点(0)からの行頭の相対位置
// 一行ずつ読み込んで解析する
while((buffer = reader.readLine()) != null)
{
analyzeOneLine(buffer,current_position);
current_position += buffer.length() + 1;
}
// 残っている閉じ忘れタグを閉じる
while(stack.empty() == false)
{
int position = text.length();
String unclosed_tag = (String)stack.pop();
list.addElement(new UnclosedTagInfo(position,unclosed_tag));
}
// ストリームを閉じる
reader.close();
string_reader.close();
}
catch(IOException ioe)
{
System.err.println(ioe);
}
// エラーリストを返却する
UnclosedTagInfo return_list[] = new UnclosedTagInfo[list.size()];
list.copyInto(return_list);
return(return_list);
}
// タグの閉じ忘れチェックルーチン(一行)
protected void analyzeOneLine(String line,int current_position)
{
int leftIndex = 0,rightIndex,spaceIndex;
// '<'がある場合
while((leftIndex = line.indexOf('<',leftIndex)) != -1)
{
// '>'がある場合
if((rightIndex = line.indexOf('>',leftIndex + 1)) != -1)
{
// タグの名前の取得(属性がある場合は無視する)
String tag;
if((spaceIndex = line.substring(leftIndex + 1,rightIndex).indexOf(' ')) != -1)
{
tag = line.substring(leftIndex + 1,leftIndex + spaceIndex + 1);
}
else
{
tag = line.substring(leftIndex + 1,rightIndex);
}
// '/'が先頭にある場合
if(!stack.empty() && tag.charAt(0) == '/')
{
// 許容タグリストにあれば
if(check_tag_list.contains(tag.substring(1,tag.length())))
{
// 閉じ忘れが無くなるまで、閉じ忘れリストに追加
while(!stack.empty() && tag.substring(1,tag.length()).compareToIgnoreCase((String)stack.peek()) != 0)
{
int position = current_position + leftIndex;
String unclosed_tag = (String)stack.pop();
list.addElement(new UnclosedTagInfo(position,unclosed_tag));
}
// 一致したタグをスタックから取り除く
// スタックが空 -> 閉じタグのみ存在するケース
if(!stack.empty())
{
stack.pop();
}
}
}
// 単なるタグの場合、スタックにプッシュする
else
{
for(int i=0;i<check_tag_list.size();i++)
{
// 許容タグリストにあれば
if(check_tag_list.contains(tag.toLowerCase()))
{
stack.push(tag.toLowerCase());
break;
}
}
}
// タグの次の文字から解析開始
leftIndex = rightIndex + 1;
}
else
{
break;
}
}
}
}
////////////////////////////////////////////////////////////
//////////////////// UnclosedTagInfo.java ////////////////////
// タグ閉じ忘れ位置とタグ名の情報
class UnclosedTagInfo
{
int position; // 挿入位置
String unclosed_tag; // タグ名
// コンストラクタ
public UnclosedTagInfo(int position,String unclosed_tag)
{
this.position = position;
this.unclosed_tag = unclosed_tag;
}
// 挿入位置の取得
public int getPosition()
{
return(position);
}
// タグ名の取得
public String getTagName()
{
return(unclosed_tag);
}
// java.lang.Object.toString()のオーバーライド
public String toString()
{
return(unclosed_tag + " : " + position);
}
}
////////////////////////////////////////////////////////////
Source is here. (ZIP Format,2624Byte,Shift-JIS)