OSDN Git Service

キーワード検索の条件追加ほか
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / SearchProgram.java
1 package tainavi;\r
2 \r
3 import java.io.File;\r
4 import java.util.ArrayList;\r
5 import java.util.regex.Matcher;\r
6 import java.util.regex.Pattern;\r
7 \r
8 import tainavi.SearchKey.TargetId;\r
9 import tainavi.TVProgram.ProgFlags;\r
10 import tainavi.TVProgram.ProgGenre;\r
11 import tainavi.TVProgram.ProgOption;\r
12 import tainavi.TVProgram.ProgScrumble;\r
13 import tainavi.TVProgram.ProgSubgenre;\r
14 \r
15 \r
16 public class SearchProgram {\r
17 \r
18         //\r
19         private ArrayList<SearchKey> searchKeys = new ArrayList<SearchKey>();\r
20 \r
21         private String searchKeyFile = null;\r
22         protected void setSearchKeyFile(String s) { searchKeyFile = s; }\r
23         \r
24         private String searchKeyLabel = null;\r
25         protected void setSearchKeyLabel(String s) { searchKeyLabel = s; }\r
26         \r
27         // 設定ファイルに書き出し\r
28         public void save() {\r
29                 System.out.println(searchKeyLabel+"設定を保存します: "+searchKeyFile);\r
30                 if ( ! CommonUtils.writeXML(searchKeyFile, searchKeys) ) {\r
31                         System.err.println(searchKeyLabel+"設定の保存に失敗しました.");\r
32                         return;\r
33                 }\r
34         }\r
35         \r
36         // 設定ファイルから読み出し\r
37         public void load() {\r
38                 System.out.println(searchKeyLabel+"設定を読み込みます: "+searchKeyFile);\r
39                 searchKeys = (ArrayList<SearchKey>) CommonUtils.readXML(searchKeyFile);\r
40                 if ( searchKeys == null ) {\r
41                         System.err.println(searchKeyLabel+"設定を読み込めなかったので登録なしで起動します.");\r
42                         searchKeys = new ArrayList<SearchKey>();\r
43                         return;\r
44                 }\r
45 \r
46                 // もういらないのでは…\r
47                 for (SearchKey sk : searchKeys) {\r
48                         // 後方互換用・その1\r
49                         compile(sk);\r
50                         // 後方互換用・その2\r
51                         if (sk.getOkiniiri() == null) {\r
52                                 sk.setOkiniiri("★");\r
53                         }\r
54                 }\r
55         }\r
56         \r
57         // 検索用\r
58         public ArrayList<SearchKey> getSearchKeys() {\r
59                 return(searchKeys);\r
60         }\r
61         \r
62         // キーワード検索の追加\r
63         public void add(SearchKey newkey) {\r
64                 searchKeys.add(newkey);\r
65         }\r
66         \r
67         public void add(int index, SearchKey newkey) {\r
68                 searchKeys.add(index, newkey);\r
69         }\r
70         \r
71         // キーワード検索の削除\r
72         public void remove(String key) {\r
73                 for ( SearchKey k : searchKeys ) {\r
74                         if (k.getLabel().equals(key)) {\r
75                                 searchKeys.remove(k);\r
76                                 break;\r
77                         }\r
78                 }\r
79         }\r
80         \r
81         // キーワード検索の置き換え\r
82         public SearchKey replace(String key, SearchKey sk) {\r
83                 int i = 0;\r
84                 for ( SearchKey k : searchKeys ) {\r
85                         if (k.getLabel().equals(key)) {\r
86                                 searchKeys.add(i,sk);\r
87                                 searchKeys.remove(i+1);\r
88                                 return(sk);\r
89                         }\r
90                         i++;\r
91                 }\r
92                 return(null);\r
93         }\r
94         \r
95         // キーワード検索の整理\r
96         public void compile(SearchKey keyword) {\r
97                 //\r
98                 keyword.alTarget = new ArrayList<TargetId>();\r
99                 keyword.alKeyword_regex = new ArrayList<Pattern>();\r
100                 keyword.alKeyword = new ArrayList<String>();\r
101                 keyword.alContain = new ArrayList<String>();\r
102                 \r
103                 //\r
104                 ArrayList<String> sTtmp = new ArrayList<String>();\r
105                 ArrayList<String> mRtmp = new ArrayList<String>();\r
106                 ArrayList<String> sCtmp = new ArrayList<String>();\r
107                 \r
108                 Matcher ma = null;\r
109                 \r
110                 //\r
111                 ma = Pattern.compile("(.*?)\t").matcher(keyword.getTarget());\r
112                 while (ma.find()) {\r
113                         sTtmp.add(String.valueOf(ma.group(1)));\r
114                 }\r
115                 //\r
116                 ma = Pattern.compile("(.*?)\t").matcher(keyword.getKeyword());\r
117                 while (ma.find()) {\r
118                         mRtmp.add(ma.group(1));\r
119                 }\r
120                 //\r
121                 ma = Pattern.compile("(.*?)\t").matcher(keyword.getContain());\r
122                 while (ma.find()) {\r
123                         sCtmp.add(String.valueOf(ma.group(1)));\r
124                 }\r
125                 \r
126                 // 「含む」を前に、「除く」を後に\r
127                 for (int x=0; x<sTtmp.size(); x++) {\r
128                         if (sCtmp.get(x).equals("0")) {\r
129                                 setAlKeyword(keyword, x, sTtmp.get(x), mRtmp.get(x), sCtmp.get(x));\r
130                         }\r
131                 }\r
132                 for (int x=0; x<sTtmp.size(); x++) {\r
133                         if (sCtmp.get(x).equals("1")) {\r
134                                 setAlKeyword(keyword, x, sTtmp.get(x), mRtmp.get(x), sCtmp.get(x));\r
135                         }\r
136                 }\r
137         }\r
138         private void setAlKeyword(SearchKey keyword, int x, String sT, String mR, String sC) {\r
139                 \r
140                 TargetId ti = TargetId.getTargetId(sT);\r
141                 keyword.alTarget.add(ti);\r
142 \r
143                 // 正規表現なターゲット\r
144                 switch (ti) {\r
145                 case TITLEANDDETAIL:\r
146                 case TITLE:\r
147                 case DETAIL:\r
148                 case CHANNEL:\r
149                         if (keyword.getCaseSensitive() == false) {\r
150                                 keyword.alKeyword_regex.add(Pattern.compile("("+TraceProgram.replacePop(mR)+")"));\r
151                         }\r
152                         else {\r
153                                 keyword.alKeyword_regex.add(Pattern.compile("("+mR+")"));\r
154                         }\r
155                         break;\r
156                 case STARTDATETIME:\r
157                         keyword.alKeyword_regex.add(Pattern.compile("("+mR+")"));\r
158                         break;\r
159                 default:\r
160                         keyword.alKeyword_regex.add(null);\r
161                         break;\r
162                 }\r
163                 \r
164                 // 数値なターゲット\r
165                 switch (ti) {\r
166                 case LENGTH:\r
167                         Matcher ma = Pattern.compile("^(\\d+) ").matcher(mR);\r
168                         if (ma.find()) {\r
169                                 keyword.alLength.add(Integer.valueOf(ma.group(1)));\r
170                         }\r
171                         else {\r
172                                 keyword.alLength.add(0);\r
173                         }\r
174                         break;\r
175                 default:\r
176                         keyword.alLength.add(0);\r
177                         break;\r
178                 }\r
179                 \r
180                 // 時刻なターゲット\r
181                 switch (ti) {\r
182                 case STARTA:\r
183                 case STARTZ:\r
184                         Matcher ma = Pattern.compile("^(\\d\\d)(:\\d\\d)").matcher(mR);\r
185                         if (ma.find()) {\r
186                                 Integer t = Integer.valueOf(ma.group(1));\r
187                                 t += (CommonUtils.isLateNight(t)?(24):(0));\r
188                                 keyword.alKeyword_plane.add(String.format("%02d%s", t, ma.group(2)));\r
189                                 //System.out.println(keyword.alKeyword_plane.get(keyword.alKeyword_plane.size()-1));\r
190                         }\r
191                         else {\r
192                                 keyword.alKeyword_plane.add("");\r
193                         }\r
194                         break;\r
195                 default:\r
196                         keyword.alKeyword_plane.add(mR);\r
197                         break;\r
198                 }\r
199                 \r
200                 keyword.alKeyword.add(TraceProgram.replacePop(mR));\r
201                 keyword.alKeyword_pop.add(TraceProgram.replacePop(mR));\r
202                 keyword.alContain.add(sC);\r
203         }\r
204         \r
205         \r
206         \r
207         //\r
208         private static int compareDT(String target, String limit)\r
209         {\r
210                 Matcher ma = Pattern.compile("^(\\d\\d)(:\\d\\d)").matcher(target);\r
211                 if (ma.find()) {\r
212                         Integer t = Integer.valueOf(ma.group(1));\r
213                         t += (CommonUtils.isLateNight(t)?(24):(0));\r
214                         String ts = String.format("%02d%s", t, ma.group(2));\r
215                         return ts.compareTo(limit);\r
216                 }\r
217                 else {\r
218                         return 0;\r
219                 }\r
220         }\r
221         \r
222         private static String matchedString = null;\r
223         public static String getMatchedString() { return matchedString; }\r
224         \r
225         public static boolean isMatchKeyword(SearchKey keyword, String center, ProgDetailList tvd)\r
226         {\r
227                 //\r
228                 ArrayList<TargetId> sT = keyword.alTarget;\r
229                 ArrayList<Pattern> mRe = keyword.alKeyword_regex;\r
230                 //ArrayList<String> mR = keyword.alKeyword;\r
231                 ArrayList<String> mRp = keyword.alKeyword_plane;\r
232                 //ArrayList<String> mRpop = keyword.alKeyword_pop;\r
233                 ArrayList<String> sC = keyword.alContain;\r
234                 ArrayList<Integer> sL = keyword.alLength;\r
235                 \r
236                 matchedString = null;\r
237 \r
238                 //\r
239                 Matcher mx = null;\r
240                 boolean isOrMatch = false;\r
241                 for (int x=0; x<sT.size(); x++) {\r
242                         //\r
243                         boolean isCurMatch = false;\r
244                         TargetId ti = sT.get(x);\r
245                         \r
246                         // 0:"番組名、内容に"\r
247                         // 1:"番組名に"\r
248                         if (ti == TargetId.TITLE || ti == TargetId.TITLEANDDETAIL) {\r
249                                 if (keyword.getCaseSensitive() == false) {\r
250                                         mx = mRe.get(x).matcher(tvd.titlePop);\r
251                                         if (mx.find()) {\r
252                                                 isCurMatch = true;\r
253                                                 if (matchedString == null) {\r
254                                                         int srcLen = tvd.title.length();\r
255                                                         int keyLen = mx.group(1).length();\r
256                                                         int keyIdx = tvd.titlePop.indexOf(mx.group(1));\r
257                                                         char[] ch = tvd.title.toCharArray();\r
258                                                         int cnt = 0;\r
259                                                         int matchIdx = -1;\r
260                                                         int matchLen = 0;\r
261                                                         for (int i=0; i<srcLen; i++) {\r
262                                                                 if ( ! TraceProgram.isOmittedChar(ch[i])) {\r
263                                                                         cnt++;\r
264                                                                 }\r
265                                                                 if (matchIdx < 0 && keyIdx == cnt-1) {\r
266                                                                         matchIdx = i;\r
267                                                                         cnt = 0;\r
268                                                                 }\r
269                                                                 if (matchIdx >= 0 && cnt < keyLen) {\r
270                                                                         matchLen++;\r
271                                                                 }\r
272                                                         }\r
273                                                         matchedString = tvd.title.substring(matchIdx,matchIdx+matchLen);\r
274                                                 }\r
275                                         }\r
276                                 }\r
277                                 else {\r
278                                         if (tvd.title.indexOf(mRp.get(x)) >= 0) {\r
279                                                 isCurMatch = true;\r
280                                                 if (matchedString == null) {\r
281                                                         matchedString = mRp.get(x);\r
282                                                 }\r
283                                         }\r
284                                 }\r
285                         }\r
286                         \r
287                         // 0:"番組名、内容に"\r
288                         // 2:"番組内容に"\r
289                         if (ti == TargetId.DETAIL || ti == TargetId.TITLEANDDETAIL) {\r
290                                 if (keyword.getCaseSensitive() == false) {\r
291                                         mx = mRe.get(x).matcher(tvd.detailPop);\r
292                                         if (mx.find()) {\r
293                                                 isCurMatch = true;\r
294                                         }\r
295                                 }\r
296                                 else {\r
297                                         if (tvd.detail.indexOf(mRp.get(x)) >= 0) {\r
298                                                 isCurMatch = true;\r
299                                         }\r
300                                 }\r
301                         }\r
302 \r
303                         switch ( ti ) {\r
304                         case CHANNEL:   // 3\r
305                                 if (keyword.getCaseSensitive() == false) {\r
306                                         mx = mRe.get(x).matcher(center);\r
307                                         if (mx.find()) {\r
308                                                 isCurMatch = true;\r
309                                         }\r
310                                 }\r
311                                 else {\r
312                                         if (center.indexOf(mRp.get(x)) >= 0) {\r
313                                                 isCurMatch = true;\r
314                                         }\r
315                                 }\r
316                                 break;\r
317                         \r
318                         case GENRE:     //4,15\r
319                                 ProgGenre gr = ProgGenre.get(mRp.get(x));\r
320                                 if ( gr != null ) {\r
321                                         isCurMatch = tvd.isEqualsGenre(gr, null);\r
322                                 }\r
323                                 break;\r
324                         case SUBGENRE:\r
325                                 ProgSubgenre sgr = ProgSubgenre.get(mRp.get(x));\r
326                                 if ( sgr != null ) {\r
327                                         isCurMatch = tvd.isEqualsGenre(null, sgr);\r
328                                 }\r
329                                 break;\r
330                         \r
331                         case NEW:       // 5,6,7,8,12,13\r
332                                 if (tvd.flag == ProgFlags.NEW) {\r
333                                         isCurMatch = true;\r
334                                 }\r
335                                 break;\r
336                         case LAST:\r
337                                 if (tvd.flag == ProgFlags.LAST) {\r
338                                         isCurMatch = true;\r
339                                 }\r
340                                 break;\r
341                         case REPEAT:\r
342                                 for (ProgOption o : tvd.getOption()) {\r
343                                         if (o == ProgOption.REPEAT) {\r
344                                                 isCurMatch = true;\r
345                                                 break;\r
346                                         }\r
347                                 }\r
348                                 break;\r
349                         case FIRST:\r
350                                 for (ProgOption o : tvd.getOption()) {\r
351                                         if (o == ProgOption.FIRST) {\r
352                                                 isCurMatch = true;\r
353                                                 break;\r
354                                         }\r
355                                 }\r
356                                 break;\r
357                         case SPECIAL:\r
358                                 for (ProgOption o : tvd.getOption()) {\r
359                                         if (o == ProgOption.SPECIAL) {\r
360                                                 isCurMatch = true;\r
361                                                 break;\r
362                                         }\r
363                                 }\r
364                                 break;\r
365                         case RATING:\r
366                                 for (ProgOption o : tvd.getOption()) {\r
367                                         if (o == ProgOption.RATING) {\r
368                                                 isCurMatch = true;\r
369                                                 break;\r
370                                         }\r
371                                 }\r
372                                 break;\r
373                         case NOSCRUMBLE:\r
374                                 if (tvd.noscrumble == ProgScrumble.NOSCRUMBLE) {\r
375                                         isCurMatch = true;\r
376                                 }\r
377                                 break;\r
378                         case LIVE:\r
379                                 for (ProgOption o : tvd.getOption()) {\r
380                                         if (o == ProgOption.LIVE) {\r
381                                                 isCurMatch = true;\r
382                                                 break;\r
383                                         }\r
384                                 }\r
385                                 break;\r
386                         case BILINGUAL:\r
387                                 for (ProgOption o : tvd.getOption()) {\r
388                                         if (o == ProgOption.BILINGUAL) {\r
389                                                 isCurMatch = true;\r
390                                                 break;\r
391                                         }\r
392                                 }\r
393                                 break;\r
394                         case MULTIVOICE:\r
395                                 for (ProgOption o : tvd.getOption()) {\r
396                                         if (o == ProgOption.MULTIVOICE) {\r
397                                                 isCurMatch = true;\r
398                                                 break;\r
399                                         }\r
400                                 }\r
401                                 break;\r
402                         case STANDIN:\r
403                                 for (ProgOption o : tvd.getOption()) {\r
404                                         if (o == ProgOption.STANDIN) {\r
405                                                 isCurMatch = true;\r
406                                                 break;\r
407                                         }\r
408                                 }\r
409                                 break;\r
410                         \r
411                         case LENGTH:    // 9\r
412                                 if (tvd.length >= sL.get(x)) {\r
413                                         isCurMatch = true;\r
414                                 }\r
415                                 break;\r
416 \r
417                         case STARTA:    // 10,11,14\r
418                                 if (compareDT(tvd.start,mRp.get(x)) >= 0) {\r
419                                         isCurMatch = true;\r
420                                 }\r
421                                 break;\r
422                         case STARTZ:\r
423                                 if (compareDT(tvd.start,mRp.get(x)) <= 0) {\r
424                                         isCurMatch = true;\r
425                                 }\r
426                                 break;\r
427                         case STARTDATETIME:\r
428                                 mx = mRe.get(x).matcher(tvd.startDateTime);\r
429                                 if (mx.find()) {\r
430                                         isCurMatch = true;\r
431                                 }\r
432                                 break;\r
433                                 \r
434                         default:\r
435                                 break;\r
436                         }\r
437                         \r
438                         \r
439                         //\r
440                         if (keyword.getCondition().equals("0")) {\r
441                                 // 0:次のすべての条件に一致\r
442                                 if (sC.get(x).equals("0") && isCurMatch == false) {\r
443                                         // 0:"を含む番組"\r
444                                         return(false);\r
445                                 }\r
446                                 else if (sC.get(x).equals("1") && isCurMatch == true) {\r
447                                         // 1:"を含む番組を除く"\r
448                                         return(false);\r
449                                 }\r
450                         }\r
451                         else {\r
452                                 // 1:次のいずれかの条件に一致\r
453                                 if (sC.get(x).equals("0") && isCurMatch == true) {\r
454                                         // 0:"を含む番組"\r
455                                         isOrMatch = true;\r
456                                 }\r
457                                 else if (sC.get(x).equals("1") && isCurMatch == true) {\r
458                                         // 1:"を含む番組を除く"\r
459                                         \r
460                                         // ★★★ 特殊動作注意!番ナビスレ Part.11 No.274付近参照 ★★★\r
461                                         // 「含む ∪ 含む ∪ 含まない ∪ 含まない」ではなく\r
462                                         // 「( 含む ∪ 含む ) ∩ ~(含まない ∪ 含まない)」となる\r
463                                         \r
464                                         return(false);\r
465                                 }\r
466                         }\r
467                 }\r
468                 \r
469                 if (keyword.getCondition().equals("0")) {\r
470                         // 0:次のすべての条件に一致で、すべての条件に適合した場合\r
471                         return(true);\r
472                 }\r
473                 else {\r
474                         // 1:次のいずれかの条件に一致、の場合\r
475                         return(isOrMatch);\r
476                 }\r
477         }\r
478         \r
479         \r
480         \r
481         // コンストラクタ\r
482         public SearchProgram() {\r
483                 setSearchKeyFile("env"+File.separator+"keyword.xml");\r
484                 setSearchKeyLabel("キーワード検索");\r
485         }\r
486 }\r