OSDN Git Service

0953e9c68cb55cb5a87e44c517350294478c6439
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / MarkedProgramList.java
1 package tainavi;\r
2 \r
3 import java.util.ArrayList;\r
4 import java.util.Calendar;\r
5 import java.util.GregorianCalendar;\r
6 import java.util.HashMap;\r
7 import java.util.regex.Matcher;\r
8 import java.util.regex.Pattern;\r
9 \r
10 import tainavi.TVProgram.ProgOption;\r
11 import tainavi.TVProgram.ProgType;\r
12 \r
13 /**\r
14  * 検索は都度行うのではなく、番組表を読み込んだり検索条件を変更したりといったイベントの際に全件作成してしまって、表示はそれを絞り込むだけにして高速化をはかる\r
15  */\r
16 public class MarkedProgramList {\r
17         \r
18         /*\r
19          * 定数\r
20          */\r
21 \r
22         private static final String MSGID = "[検索結果生成] ";\r
23         //private static final String ERRID = "[ERROR]"+MSGID;\r
24         //private static final String DBGID = "[DEBUG]"+MSGID;\r
25         \r
26         /*\r
27          * \r
28          */\r
29         \r
30         private ArrayList<ProgDetailList> programs = null;\r
31         private ArrayList<ArrayList<TraceKey>> traceKeys = null;\r
32         private ArrayList<ArrayList<Integer>> traceScores = null;\r
33         private ArrayList<ArrayList<SearchKey>> searchKeys = null;\r
34         private ArrayList<ArrayList<String>> searchStrs = null;\r
35 \r
36         public ProgDetailList getProg(int n) { return this.programs.get(n); }\r
37         public ArrayList<TraceKey> getTKey(int n) { return this.traceKeys.get(n); }\r
38         public ArrayList<Integer> getTScore(int n) { return this.traceScores.get(n); }\r
39         public ArrayList<SearchKey> getSKey(int n) { return this.searchKeys.get(n); }\r
40         public ArrayList<String> getSStr(int n) { return this.searchStrs.get(n); }\r
41 \r
42         public int size() { return (programs==null)?(0):(programs.size()); }\r
43 \r
44         private boolean disableFazzySearch = false;\r
45         private boolean disableFazzySearchReverse = false;\r
46         \r
47         // \r
48         private boolean historyOnlyUpdateOnce = false;\r
49         public void setHistoryOnlyUpdateOnce(boolean b) { historyOnlyUpdateOnce = b; }\r
50         \r
51         //\r
52         private boolean showOnlyNonrepeated = true;\r
53         public void setShowOnlyNonrepeated(boolean b) { showOnlyNonrepeated = b; }\r
54         \r
55         \r
56         //\r
57         \r
58         public void build(ArrayList<TVProgram> progs, ArrayList<TraceKey> trKeys, ArrayList<SearchKey> srKeys) {\r
59                 // フラグを落とす\r
60                 clearMarkedFlag(progs);\r
61                 \r
62                 // 番組追跡\r
63         for (TraceKey trace : trKeys) {\r
64                 buildByKeyword(progs, trace, null);\r
65         }\r
66         // キーワード検索\r
67                 for (SearchKey search : srKeys) {\r
68                 buildByKeyword(progs, null, search);\r
69         }\r
70                 // 新着チェック\r
71                 chkNewArrival();\r
72         }\r
73         \r
74         /**\r
75          * 検索条件がかわったなら全番組のフラグを初期化しないといけない\r
76          */\r
77         private void clearMarkedFlag(ArrayList<TVProgram> tvprograms) {\r
78                 for ( TVProgram tvp : tvprograms ) {\r
79                         if (tvp.getType() != TVProgram.ProgType.PROG && tvp.getType() != TVProgram.ProgType.SYOBO) {\r
80                                 continue;\r
81                         }\r
82                         \r
83                         for ( ProgList tvpl : tvp.getCenters() ) {\r
84                                 if ( ! tvpl.enabled) {\r
85                                 continue;\r
86                         }\r
87                                 \r
88                                 for ( ProgDateList tvc : tvpl.pdate ) {\r
89                                         for ( ProgDetailList tvd : tvc.pdetail ) {\r
90                                                 tvd.marked = false;\r
91                                                 tvd.nonrepeated = false;\r
92                                                 tvd.showinstandby = false;\r
93                                         }\r
94                                 }\r
95                         }\r
96                 }\r
97         }\r
98         \r
99         private void buildByKeyword(ArrayList<TVProgram> tvprograms, TraceKey tKey, SearchKey sKye) {\r
100                 // 検索条件のマッチカウントのクリア\r
101                 SearchItem item = tKey != null ? tKey : sKye;\r
102                 item.clearMatchedList();\r
103 \r
104                 //\r
105                 for (int siteid=0; siteid<tvprograms.size(); siteid++) {\r
106                         // ほげほげ\r
107                         TVProgram tvp = tvprograms.get(siteid);\r
108                         \r
109                         if (tvp.getType() != TVProgram.ProgType.PROG && tvp.getType() != TVProgram.ProgType.SYOBO) {\r
110                                 continue;\r
111                         }\r
112                         \r
113                         for (int centerid=0; centerid<tvp.getCenters().size(); centerid++) {\r
114                                 // ほげほげ\r
115                                 ProgList tvpl = tvp.getCenters().get(centerid);\r
116                         \r
117                                 if ( ! tvpl.enabled) {\r
118                                 continue;\r
119                         }\r
120                                 \r
121                                 if (tKey != null && ! tKey.getCenter().equals(tvpl.Center) ) {\r
122                                         continue;\r
123                                 }\r
124                                 \r
125                                 if ( tKey != null && tKey.getShowLatestOnly() ) {\r
126                                         System.out.println(MSGID+"[リピート放送判定] リピート放送を排除する検索キー: *"+tvp.getType()+"* "+tKey._getLabel());\r
127                                 }\r
128                                 \r
129                                 // 一時保存用\r
130                                 MatchedBuffer mBuf = new MatchedBuffer();\r
131                         \r
132                         // キーワード検索用\r
133                         String centerPop = TraceProgram.replacePop(tvpl.Center);\r
134                         \r
135                                 for (int dateid=0; dateid<tvpl.pdate.size(); dateid++) {\r
136                                         String matchedString = null;\r
137                                         ProgDateList tvc = tvpl.pdate.get(dateid);\r
138                                         for (int progid=0; progid<tvc.pdetail.size(); progid++) {\r
139                                                 ProgDetailList tvd = tvc.pdetail.get(progid);\r
140                                                 \r
141                                                 // 番組情報がありませんは表示しない\r
142                                                 if (tvd.start.equals("")) {\r
143                                                         continue;\r
144                                                 }\r
145                                                 \r
146                                         //マッチング\r
147                                                 int fazScore = 0;\r
148                                                 boolean isFind = false;\r
149                                                 if (tKey != null) {\r
150                                                         if (tKey.getDisableRepeat() == true && tvd.isOptionEnabled(ProgOption.REPEAT)) {\r
151                                                                 // 再放送を除く\r
152                                                                 continue;\r
153                                                         }\r
154                                                         \r
155                                                         if (this.disableFazzySearch == true) {\r
156                                                                 // 完全一致\r
157                                                                 if (tKey._getTitlePop().equals(tvd.titlePop)) {\r
158                                                                         isFind = true;\r
159                                                                 }\r
160                                                         }\r
161                                                         else {\r
162                                                                 //あいまい検索・正引き\r
163                                                                 String target = ProgDetailList.tracenOnlyTitle ?  tvd.splitted_titlePop : tvd.titlePop;\r
164                                                                 fazScore = TraceProgram.sumScore(target, tKey._getTitlePop());\r
165                                                                 if (fazScore >= tKey.getFazzyThreshold()) {\r
166                                                                         isFind = true;\r
167                                                                 }\r
168                                                                 else if ( ! this.disableFazzySearchReverse) {\r
169                                                                         // 逆引き\r
170                                                                         fazScore = TraceProgram.sumScore(tKey._getTitlePop(), target);\r
171                                                                         if (fazScore >= tKey.getFazzyThreshold()) {\r
172                                                                                 isFind = true;\r
173                                                                         }\r
174                                                                 }\r
175                                                         }\r
176                                                 }\r
177                                                 else if (sKye != null) {\r
178                                                 isFind = SearchProgram.isMatchKeyword(sKye, ((sKye.getCaseSensitive()==false)?(centerPop):(tvpl.Center)), tvd);\r
179                                                 if ( isFind ) {\r
180                                                         matchedString = SearchProgram.getMatchedString();\r
181                                                 }\r
182                                         }\r
183                                                 \r
184                                                 if (isFind) {\r
185                                                         tvd.marked = true;\r
186                                                         mBuf.add(tvd, tKey, fazScore, sKye, matchedString);\r
187                                                 }\r
188                                         }\r
189                                 }\r
190                                 for ( MatchedBufferData d : mBuf.getData() ) {\r
191                                         if ( d.prog.marked ) {\r
192                                                 if ( tKey != null && tKey.getShowLatestOnly() ) {\r
193                                                         System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送ではないと判断されました: "+d.prog.startDateTime+" 「"+d.prog.title+"("+d.bareTitle+")」 ("+d.storyNo+")");\r
194                                                         d.prog.nonrepeated = true;\r
195                                                 }\r
196                                                 if ( ! (sKye != null && ! sKye.getShowInStandby()) ) {\r
197                                                         d.prog.showinstandby = true;\r
198                                                 }\r
199                                                 this.add(d.prog, d.tKey, d.tScore, d.sKey, d.sStr);\r
200                                         }\r
201                                         else {\r
202                                                 if ( tKey != null && tKey.getShowLatestOnly() ) {\r
203                                                         if ( ! showOnlyNonrepeated ) {\r
204                                                                 // 復活戦\r
205                                                                 d.prog.marked = true;\r
206                                                                 if ( sKye != null && sKye.getShowInStandby() ) {\r
207                                                                         d.prog.showinstandby = true;\r
208                                                                 }\r
209                                                                 this.add(d.prog, d.tKey, d.tScore, d.sKey, d.sStr);\r
210                                                         }\r
211                                                 }\r
212                                         }\r
213                                 }\r
214                         }\r
215                 }\r
216         }\r
217         \r
218         // 一時保存のためのサブクラス\r
219         private class MatchedBufferData {\r
220                 public ProgDetailList prog = null;\r
221                 public TraceKey tKey = null;\r
222                 public int tScore = 0;\r
223                 public SearchKey sKey = null;\r
224                 public String sStr = null;\r
225                 //\r
226                 public String bareTitle = null;\r
227                 public Integer storyNo = null;\r
228                 \r
229                 public MatchedBufferData(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
230                         this.prog = prog;\r
231                         this.tKey = tKey;\r
232                         this.tScore = tScore;\r
233                         this.sKey = sKey;\r
234                         this.sStr = sStr;\r
235                         //\r
236                         this.bareTitle = null;\r
237                         this.storyNo = null;\r
238                 }\r
239         }\r
240         private class MatchedBuffer {\r
241                 //\r
242                 public ArrayList<MatchedBufferData> data = new ArrayList<MatchedBufferData>();\r
243                 public String xDateTime = null ;\r
244                 \r
245                 // コンストラクタ\r
246                 public MatchedBuffer() {\r
247                         GregorianCalendar c = CommonUtils.getCalendar(0);\r
248                         if ( CommonUtils.isLateNight(c) ) {\r
249                                 c.add(Calendar.DATE, 6);\r
250                         }\r
251                         else {\r
252                                 c.add(Calendar.DATE, 7);\r
253                         }\r
254                         c.set(Calendar.HOUR_OF_DAY, 5);\r
255                         c.set(Calendar.MINUTE, 0);\r
256                         xDateTime = CommonUtils.getDateTime(c); \r
257                 }\r
258                 \r
259                 //\r
260                 public ArrayList<MatchedBufferData> getData() {\r
261                         return data;\r
262                 }\r
263                 \r
264                 private final String[] exprs = {\r
265                                 // AT-XやANIMAXの場合\r
266                                 "[##]([0-90-9]+)",\r
267                                 "第([0-90-9]+)[話回]",\r
268                                 // NHKの場合\r
269                                 "[((]([0-90-9]+?)[))]",\r
270                 };\r
271                 \r
272                 //\r
273                 public void add(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
274                         MatchedBufferData bd = new MatchedBufferData(prog, tKey, tScore, sKey, sStr);\r
275                         if ( tKey == null || ! tKey.getShowLatestOnly() ) {\r
276                                 // キーワード検索だったりリピート有効な場合はそのまま\r
277                                 data.add(bd);\r
278                                 return;\r
279                         }\r
280                         // 話数をとりだす\r
281                         {\r
282                                 // タイトルと番組詳細両方でしらべるもの\r
283                                 for ( String expr : exprs ) {\r
284                                         Matcher ma = Pattern.compile(expr).matcher(prog.title);\r
285                                         if ( ma.find() ) {\r
286                                                 bd.storyNo = Integer.valueOf(CommonUtils.toHANUM(ma.group(1)));\r
287                                                 break;\r
288                                         }\r
289                                         ma = Pattern.compile("^[  \t]*"+expr).matcher(prog.detail);\r
290                                         if ( ma.find() ) {\r
291                                                 bd.storyNo = Integer.valueOf(CommonUtils.toHANUM(ma.group(1)));\r
292                                                 break;\r
293                                         }\r
294                                 }\r
295                         }\r
296                         if ( bd.storyNo != null ) {\r
297                                 \r
298                                 // 半角数字にそろえりーな\r
299                                 //bd.storyNo = CommonUtils.toHANUM(bd.storyNo);\r
300                                 \r
301                                 // 同一タイトルで重複するものを排除する\r
302                                 \r
303                                 // 話数を外した裸のタイトルだけを抽出する\r
304                                 bd.bareTitle = TraceProgram.replacePop(prog.title.replaceFirst("\\s*([##][0-90-9]+|[((][0-90-9]+[))]|第[0-90-9]+[話回]).*$", ""));\r
305                                 \r
306                                 for ( MatchedBufferData d : data ) {\r
307                                         if ( d.prog.marked == false ) {\r
308                                                 // 既に無効化されていれば無視\r
309                                                 continue;\r
310                                         }\r
311                                         if ( prog.startDateTime.compareTo(xDateTime) < 0) {\r
312                                                 // 7日以内のデータ\r
313                                                 if ( d.bareTitle != null && d.bareTitle.equals(bd.bareTitle) ) {\r
314                                                         if ( d.storyNo != null && d.storyNo >= bd.storyNo ) {\r
315                                                                 // 同じかより新しいものがすでにあったら自分を捨てる\r
316                                                                 bd.prog.marked = false;\r
317                                                                 System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(すでに新しいものがある): "+bd.prog.startDateTime+" 「"+bd.prog.title+"("+bd.bareTitle+")」 ("+bd.storyNo+")");\r
318                                                         }\r
319                                                         else {\r
320                                                                 // 自分より古いものは捨てる\r
321                                                                 d.prog.marked = false;\r
322                                                                 System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(より新し番組がみつかった): "+d.prog.startDateTime+" 「"+d.prog.title+"("+d.bareTitle+")」 ("+d.storyNo+")");\r
323                                                         }\r
324                                                 }\r
325                                         }\r
326                                         else {\r
327                                                 // 8日目以降のデータは特殊扱い\r
328                                                 if ( d.bareTitle != null && d.bareTitle.equals(bd.bareTitle) ) {\r
329                                                         if ( d.storyNo != null && d.storyNo >= bd.storyNo ) {\r
330                                                                 // 同じかより新しいものがすでにあったら自分を捨てる\r
331                                                                 bd.prog.marked = false;\r
332                                                                 System.out.println(MSGID+"[リピート放送判定] [結果] リピート放送と判定されました(すでに新しいものがある[8日目]): "+bd.prog.startDateTime+" 「"+bd.prog.title+"("+bd.bareTitle+")」 ("+bd.storyNo+")");\r
333                                                         }\r
334                                                         else {\r
335                                                                 // 自分より古いものがあってもなにもしない\r
336                                                         }\r
337                                                 }\r
338                                         }\r
339                                 }\r
340                         }\r
341                         else {\r
342                                 // 話数のついてないもの\r
343                                 bd.bareTitle = TraceProgram.replacePop(prog.title);\r
344                                 bd.storyNo = -1;\r
345                         }\r
346                         data.add(bd);\r
347                 }\r
348         }\r
349                 \r
350         private void add(ProgDetailList prog, TraceKey tKey, int tScore, SearchKey sKey, String sStr) {\r
351                 \r
352                 // 検索条件のマッチカントをカウントアップ\r
353                 SearchItem item = sKey != null ? sKey : tKey; \r
354                 if (item != null && prog.type == ProgType.PROG) {\r
355                         item.addMatchedList(prog);\r
356                 }\r
357 \r
358                 // 既存に\r
359                 \r
360                 for (int n=0; n<this.programs.size(); n++) {\r
361                         if (this.programs.get(n).equals(prog)) {\r
362                                 if (tKey != null) {\r
363                                         this.traceKeys.get(n).add(tKey);\r
364                                         this.traceScores.get(n).add(tScore);\r
365                                 }\r
366                                 else if (sKey != null) {\r
367                                         this.searchKeys.get(n).add(sKey);\r
368                                         this.searchStrs.get(n).add(sStr);\r
369                                 }\r
370                                 return;\r
371                         }\r
372                 }\r
373                         \r
374                 // 新規追加\r
375 \r
376                 // 開始時刻順にソート\r
377                 int p=0;\r
378                 for (; p<this.programs.size(); p++) {\r
379                         if (this.programs.get(p).startDateTime.compareTo(prog.startDateTime) > 0) {\r
380                                 break;\r
381                         }\r
382                 }\r
383                 \r
384                 this.programs.add(p,prog);\r
385                 this.traceKeys.add(p, new ArrayList<TraceKey>());\r
386                 this.traceScores.add(p, new ArrayList<Integer>());\r
387                 this.searchKeys.add(p, new ArrayList<SearchKey>());\r
388                 this.searchStrs.add(p, new ArrayList<String>());\r
389                 if (tKey != null) {\r
390                         this.traceKeys.get(p).add(tKey);\r
391                         this.traceScores.get(p).add(tScore);\r
392                 }\r
393                 if (sKey != null) {\r
394                         this.searchKeys.get(p).add(sKey);\r
395                         this.searchStrs.get(p).add(sStr);\r
396                 }\r
397         }\r
398         \r
399         // \r
400         public void clear(boolean b1, boolean b2) {\r
401                 //\r
402                 this.disableFazzySearch = b1;\r
403                 this.disableFazzySearchReverse = b2;\r
404                 \r
405                 //\r
406                 this.programs = new ArrayList<ProgDetailList>();\r
407                 \r
408                 this.traceKeys = new ArrayList<ArrayList<TraceKey>>();\r
409                 this.traceKeys.add(new ArrayList<TraceKey>());\r
410                 this.traceScores = new ArrayList<ArrayList<Integer>>();\r
411                 this.traceScores.add(new ArrayList<Integer>());\r
412                 \r
413                 this.searchKeys = new ArrayList<ArrayList<SearchKey>>();\r
414                 this.searchKeys.add(new ArrayList<SearchKey>());\r
415                 this.searchStrs = new ArrayList<ArrayList<String>>();\r
416                 this.searchStrs.add(new ArrayList<String>());\r
417         }\r
418 \r
419         // 検索結果の履歴と突き合わせて新着をチェックする\r
420         public void chkNewArrival() {\r
421                 \r
422                 MarkedHistoryList oldhist = MarkedHistoryList.load(historyOnlyUpdateOnce);\r
423                 MarkedHistoryList newhist = new MarkedHistoryList();\r
424                 \r
425                 int max = this.size();\r
426                 for ( int i=0; i<max; i++ ) {\r
427                         \r
428                         // 通常の番組情報のみ(しょぼかるは対象外)\r
429                         if ( this.getProg(i).type != ProgType.PROG ) {\r
430                                 continue;\r
431                         }\r
432                         \r
433                         MarkedHistory mh = new MarkedHistory();\r
434                         mh.setCenter(this.getProg(i).center);\r
435                         mh.setStartDateTime(this.getProg(i).startDateTime);\r
436                         mh.setDetail(this.getProg(i).detail);\r
437                         \r
438                         // 番組追跡とキーワード検索のダブリを排除\r
439                         if ( newhist.isMatch(mh) ) {\r
440                                 continue;\r
441                         }\r
442                         \r
443                         // 新着チェック( ! isMatch()注意)\r
444                         this.getProg(i).newarrival = ! oldhist.isMatch(mh);\r
445                         this.getProg(i).modified = (this.getProg(i).newarrival)?(false):(oldhist.isModified(mh));\r
446                         \r
447                         // 履歴更新\r
448                         newhist.add(mh);\r
449                 }\r
450                 \r
451                 // 履歴を保存\r
452                 newhist.save(historyOnlyUpdateOnce);\r
453         }\r
454 }\r