package nliveroid.nlr.main.parser;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import nliveroid.nlr.main.ErrorCode;
import nliveroid.nlr.main.Gate;
import nliveroid.nlr.main.Gate.BackGroundUpdate;
import nliveroid.nlr.main.LiveInfo;
import nliveroid.nlr.main.NLiveRoid;
import nliveroid.nlr.main.URLEnum;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import android.util.Log;
public class GateParser implements ContentHandler {
private String startTag;
private Attributes nowAttr;
private StringBuilder innerText = new StringBuilder(1024);
private final LiveInfo liveinfo;
private boolean titleSet = false;
private boolean ownerSet = false;
private boolean cnameSet = false;
private int section = 0;//<section>がでてくるのが、放送詳細、コミュ詳細、タグの順
private boolean commuInfoSet = false;
private boolean tagsection = false;
private boolean isInfo = false;
private boolean isChannel = false;
private final BackGroundUpdate task;
private String masterDescStr = "";
private String masterTagsStr = "";
private String masterComminfoStr = "";
private final String sizeStr = "size";
private final String colorStr = "color";
private ErrorCode error;//最初のパースとサムネイル取得に必要
private int isFirstArticle;
private int linkIndex = 0;
private final Pattern reservept = Pattern.compile("gate_timeshift_0_(community|channel|official)_lv[0-9]+_comingsoon");
private boolean isFinished;
public GateParser(Gate.BackGroundUpdate task,ErrorCode error,LiveInfo defaultLiveinfo){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," new GateParser " + defaultLiveinfo.getLiveID());
this.task = task;
this.error = error;
this.liveinfo = defaultLiveinfo;
}
private String getInnerText(char[] arg0,int arg2){
innerText = innerText.delete(0,arg0.length);
innerText.append(arg0, 0, arg2);
return innerText.toString();
}
//ここはendTagの時にも呼ばれる気がするのでstartElementでのinnerTextがこっちと共通してるとは限らない
@Override
public void characters(char[] arg0, int arg1, int arg2) throws SAXException {
if(startTag.equals("h2")&&!titleSet&&nowAttr != null && nowAttr.getLength() > 0){//放送タイトル
for(int i = 0; i < nowAttr.getLength();i++){
if(nowAttr.getLocalName(i).equals("class")&&nowAttr.getValue(i).equals("title")){
getInnerText(arg0, arg2);
String title = innerText.toString().replaceAll("\t|\n", "");
if(liveinfo != null){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse title" + title);
liveinfo.setTitle(title);
titleSet = true;
}
}
}
}else if(startTag.equals("p")&&!ownerSet &&nowAttr != null && nowAttr.getLength() > 0
&&nowAttr.getValue("class")!=null&&nowAttr.getValue("class").equals("desc")){//放送者名
String ownname = getInnerText(arg0, arg2).replaceAll("\t|\n|放送者:", "");
if(ownname != null && ownname.equals("放送チャンネル:")){//チャンネルは、放送者名が「放送チャンネル:」となっていて、そのなかに<a>でURLと放送チャンネル者名がある
ownerSet = true;
isChannel = true;
commuInfoSet = true;
return;
}
if(liveinfo != null){
liveinfo.setOwnerName(ownname);
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse ownname" + ownname);
ownerSet = true;
}
}else if(startTag.equals("a")&&!cnameSet&&ownerSet){//コミュニティ名
String inner =getInnerText(arg0,arg2).replaceAll("\t|\n", "");
if(liveinfo != null){
if(commuInfoSet){//すでにインフォセットフラグがあるのはチャンネルとする
liveinfo.setOwnerName(inner);
if(nowAttr != null && nowAttr.getValue("onclick")!= null){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse setComunityName");
liveinfo.setComunityName(nowAttr.getValue("onclick").split("/")[4].split("'")[0]);
}
}else{
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse setComunityName innerX " + inner);
liveinfo.setComunityName(inner);
//coが無かったらここでセットする(chはGateからのコミュニティ系機能に非対応なので無視)
if(liveinfo.getCommunityID().equals(URLEnum.HYPHEN) && nowAttr != null && nowAttr.getValue("onclick") != null){
Matcher copt = Pattern.compile("co[0-9]+").matcher(nowAttr.getValue("onclick"));
if(copt.find()){
liveinfo.setCommunityID(copt.group());
}
}
}
cnameSet = true;
}
}else if(isInfo){
//ここで<section><h2>お知らせ</h2>XXXX/XX/XXに終了||開始しますの、テキスト</section>
//となっているのでstartElement、endElementではテキストが取れない
String inner = getInnerText(arg0,arg2).replaceAll("\t|\n| | ", "");
Matcher mc = Pattern.compile("[0-9]{4}/[0-9]{2}/[0-9]{2}.*").matcher(inner);
if(mc.find()){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse <section><h2>OSHIRASE ");
masterDescStr += mc.group();
}
}else if(section == 1){//放送詳細の文字列
String inner = getInnerText(arg0,arg2).replaceAll("\n","");
if(startTag.equals("h2")&&innerText.toString().contains("お知らせ")){
isInfo = true;//お知らせセクションがコミュ詳細の上に来る→終了していた||まだ始まっていない
section--;
return;
}
if(inner.length() < 1)return;
//詳細の文字列前にタブ文字などがあり、綺麗に表示できない
//なぜか"^ |^ |^\t"にマッチしないのに最初にスペースが入ってくる
//何行番目で記事がはじまるのか統一されていない
if(isFirstArticle <= 5){//最初の5文字までにタブ文字等があると消える
for(int i = 0; i < inner.length(); i++){
if(inner.substring(0,1).matches("\n| |\t| ")){
inner = inner.substring(1);
i--;
}else{
break;
}
}
if(inner.contains("ID"))inner = inner.replaceAll("番組紹介文|番組|\\(|\\)|ID|:","")+"<br><br>";
}
if(inner.matches(".++")){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse Desc " + inner);
masterDescStr += inner;
}
}else if(section == 2){//コミュニティ情報の文字列
if(startTag.equals("div")&&!commuInfoSet){
if(nowAttr != null){
if(nowAttr.getValue("id") != null && nowAttr.getValue("id").equals("levelsection")){//レベル
masterComminfoStr += getInnerText(arg0,arg2).replaceAll("\t| | |\n", "");
}else if(nowAttr.getValue("id") != null && nowAttr.getValue("id").equals("countsection")){//メンバー
masterComminfoStr += "<br>" + getInnerText(arg0,arg2).replaceAll("\t| | |\n", "") ;
}else if(nowAttr.getValue("id") != null && nowAttr.getValue("id").equals("descriptionsection")){
commuInfoSet = true;//コミュニティプロフィールっていう文字列も、いらないので飛ばす
}
}
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse CommuInfo ");
}else if(commuInfoSet||isChannel){//チャンネルの場合、レベルとメンバーは来ない
String inner = getInnerText(arg0,arg2).replaceAll("\n","");
if(inner.length() < 1)return;
//何行番目で記事がはじまるのか統一されていない
if(isFirstArticle <= 10){//最初の10文字までにタブ文字等があると消える
for(int i = 0; i < inner.length(); i++){
if(inner.substring(0,1).matches(" |\t| ")){
inner = inner.substring(1);
i--;
}else{
break;
}
}
}
if(isChannel){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse ");
if(startTag.equals("h2") && nowAttr != null && nowAttr.getValue("class") != null
&&nowAttr.getValue("class").equals("hd")){//<h2 class="hd">チャンネル情報</h2>は消す
inner = inner.replaceAll("チャンネル情報|\n| |\t| ", "");//chXXX改行とするなら、この次のcharacters時にくる時に改行入れないといけない
}
//<span class="btn_text">で終了
if(startTag.equals("span") && nowAttr != null && nowAttr.getValue("class")!=null
&&nowAttr.getValue("class").equals("btn_text")){
return;
}
}
if(inner.matches(".+")){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse inner.matches(\".+\")" + masterComminfoStr.length());
masterComminfoStr +=inner;
}
}
}else if(tagsection&&startTag.equals("option")){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse Live Tag set");
masterTagsStr += "<<TAGXXX>>"+getInnerText(arg0,arg2);
}
// Log.d("GateParser","char " + getInnerText(arg0,arg2).replace("\n",""));
}
@Override
public void startElement(String arg0, String arg1, String arg2,
Attributes arg3) throws SAXException {
startTag=arg1;
nowAttr = arg3;
if(isFinished)return;
// if(startTag.equals("footer")){//footerタグが無くなっていた ので</body>で終了判定にした 2014 1/27
// if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse footer finishCallBack --- ");
// task.finishCallBack(liveinfo);
// isFinished = true;
// return;
// }else
if(startTag.equals("select") && arg3 != null && arg3.getLength() > 0 && arg3.getValue(0).equals("selecttag")){
tagsection = true;//ここで登録タグのパースが開始される この後optionタグはないので、このフラグを終了フラグに使う→ここのoptionタグがあったらできなくなっちゃう事に注意
if(NLiveRoid.isDebugMode)Log.d("NLR"," GateParse tagsection");
}else if(section == 0 && startTag.equals("a") && nowAttr != null && nowAttr.getValue("onclick") != null){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid","FINDATTR --- " + nowAttr.getValue("onclick"));
Matcher mc = reservept.matcher(nowAttr.getValue("onclick"));
if(mc.find()){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid","FINDMATCHER --- ");
liveinfo.setTsReserveToken(mc.group());
}
}else if(startTag.equals("section")){//<section>がでてくるのが、放送詳細、コミュ詳細、タグの順
section++;
isFirstArticle = 0;
}
if(section == 1){
if(isFirstArticle < 10){//放送詳細、コミュインフォは2行目?から
isFirstArticle ++;
}
masterDescStr += isTag_Start(arg1,arg3);
}else if(section == 2){
if(isFirstArticle < 10){
//詳細、コミュインフォは2行目?から
isFirstArticle ++;
}
if(nowAttr != null && nowAttr.getLength() > 0
&&nowAttr.getValue("id")!=null&&nowAttr.getValue("id").equals("alertsection")){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," END Community description");
section = 3;//アラート登録とかの前に次のセクションに行ってコミュ詳細文面のパースを終了する
return;
}
masterComminfoStr += isTag_Start(arg1,arg3);
}
}
@Override
public void endDocument() throws SAXException {
}
@Override
public void endElement(String arg0, String arg1, String arg2)
throws SAXException {
if(tagsection&&arg1.equals("div")){
Log.d("NLiveRoid"," GateParse tagsection && div finishCallBack");
task.finishCallBack(liveinfo);
isFinished = true;
return;
}
if(arg1.equals("select")){
// tagsection = false;//ここで登録タグのパースが終了する この後
if(liveinfo != null){
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid"," GateParse tagsection = false");
liveinfo.setTags(masterTagsStr);
liveinfo.setDescription(masterDescStr);
liveinfo.setCommunity_info(masterComminfoStr);
}
}
//放送詳細か、コミュ詳細で、今ついになる予定のタグがきて、且つ
//charactersが呼ばれたので空タグじゃない(<i></i>みたいのじゃない)
if(section == 1 ){//放送詳細
masterDescStr += isTag_End(arg1);
}else if(section == 2 && commuInfoSet){
//コミュプロフィール
masterComminfoStr += isTag_End(arg1);
}
if(isInfo&&arg1.equals("section")){//終了またはまだ開始されてない
isInfo = false;
}
}
@Override
public void endPrefixMapping(String arg0) throws SAXException {
// TODO
}
@Override
public void ignorableWhitespace(char[] arg0, int arg1, int arg2)
throws SAXException {
// TODO
}
@Override
public void processingInstruction(String arg0, String arg1)
throws SAXException {
// TODO
}
@Override
public void setDocumentLocator(Locator arg0) {
// TODO
}
@Override
public void skippedEntity(String arg0) throws SAXException {
// TODO
}
@Override
public void startDocument() throws SAXException {
// TODO
}
@Override
public void startPrefixMapping(String arg0, String arg1)
throws SAXException {
// TODO
}
/**
* 詳細、コミュニティインフォタグを返す スタートタグ
* @param String startTag
* @param String endTag
* @return result
*/
private String isTag_Start(String tag,Attributes attr){
String result = "";
//startTagは次のタグを見に行っていて、endTagより先に呼ばれている
//常にstartTag,endTagはあるので改行のみならスキップ
//innerの終わり判定がうまくいかずその時のstartTagが付いてしまうので
//実際に色などつける時(Spannable)にタグの終わりがなければシカト処理が必要
//brはソースに含まれている\nが入ってるので無視できる
if(!tag.replaceAll("\n","").matches(".++")){
return "";
}
if(tag.equals("font")){//大文字でもこれでちゃんと入ってくる、Valueが空文字の場合があるので注意
if(attr != null && attr.getLength() > 0){
String size = attr.getValue(sizeStr) != null? attr.getValue(sizeStr).equals("")? " size=1":" size="+attr.getValue(sizeStr):"";
String color = attr.getValue(colorStr) != null? attr.getValue(colorStr).equals("")? " color=#000000":" color="+attr.getValue(colorStr):"";
result += "<font" +size + color+">";
}
}else if(tag.equals("b")){
result += "<b>";
}else if(tag.equals("i")){
result += "<i>";
}else if(tag.equals("s")){
result += "<s>";
}else if(tag.equals("u")){//uはaに取って代わられるようなので付けない
result += "<u>";
}else if(tag.equals("a")){
String url = "";
for(int i = 0; i < attr.getLength() ; i++){
if(attr.getLocalName(i).equals("href")&&!attr.getValue(i).contains("javascript:void(0)")){
url = attr.getValue(i);
}
}
result += "<<LINK" + linkIndex+">>" + url+"<<LINK"+linkIndex+">>";
linkIndex++;
}else if(tag.equals("br")){//改行はスタートだけでとる
result += "<br>";
}
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid","Tag_Start" + result);
return result;
}
/**
* 詳細、コミュニティインフォタグを返す エンドタグ
* @param String startTag
* @param String endTag
* @return result
*/
private String isTag_End(String tag){
String result = "";
//startTagは次のタグを見に行っていて、endTagより先に呼ばれている
//常にstartTag,endTagはあるので改行のみならスキップ
//innerの終わり判定がうまくいかずその時のstartTagが付いてしまうので
//実際に色などつける時(Spannable)にタグの終わりがなければシカト処理が必要
//brはソースに含まれている\nが入ってるので無視できる
if(!tag.replaceAll("\n","").matches(".++")){
return "";
}
if(tag.equals("font")){
result += "</font>";
}else if(tag.equals("b")){
result += "</b>";
}else if(tag.equals("i")){
result += "</i>";
}else if(tag.equals("s")){
result += "</s>";
}else if(tag.equals("u")){
result += "</u>";
}
if(NLiveRoid.isDebugMode)Log.d("NLiveRoid","Tag_End " + result);
return result;
}
}