OSDN Git Service

first commit
[tainavi/TinyBannavi.git] / TinyBannavi / src / tainavi / TVProgramUtils.java
1 package tainavi;\r
2 \r
3 import java.io.BufferedInputStream;\r
4 import java.io.BufferedOutputStream;\r
5 import java.io.BufferedReader;\r
6 import java.io.BufferedWriter;\r
7 import java.io.File;\r
8 import java.io.FileOutputStream;\r
9 import java.io.FileWriter;\r
10 import java.io.IOException;\r
11 import java.io.InputStreamReader;\r
12 import java.io.OutputStream;\r
13 import java.net.HttpURLConnection;\r
14 import java.net.InetSocketAddress;\r
15 import java.net.MalformedURLException;\r
16 import java.net.Proxy;\r
17 import java.net.URL;\r
18 import java.util.ArrayList;\r
19 import java.util.Calendar;\r
20 import java.util.Collections;\r
21 import java.util.Date;\r
22 import java.util.GregorianCalendar;\r
23 import java.util.HashMap;\r
24 import java.util.List;\r
25 import java.util.regex.Matcher;\r
26 import java.util.regex.Pattern;\r
27 \r
28 import tainavi.TVProgram.ProgFlags;\r
29 import tainavi.TVProgram.ProgGenre;\r
30 import tainavi.TVProgram.ProgOption;\r
31 import tainavi.TVProgram.ProgScrumble;\r
32 import tainavi.TVProgram.ProgSubgenre;\r
33 \r
34 \r
35 /**\r
36  * {@link TVProgram}インタフェース をインプルメントしたWeb番組表プラグインのクラスで利用できる、共有部品の集合です。\r
37  */\r
38 public class TVProgramUtils implements Cloneable {\r
39 \r
40         /*******************************************************************************\r
41          * ディープコピーが意外と大変\r
42          ******************************************************************************/\r
43         \r
44         @Override\r
45         public TVProgramUtils clone() {\r
46                 try {\r
47                         TVProgramUtils p = (TVProgramUtils) super.clone();\r
48                         \r
49                         // フィールドコピーしてもらいたくないもの\r
50                         p.pcenter = null;\r
51                         \r
52                         // static化したのでコピー抑制を必要としなくなったものたち\r
53                         //p.setProgressArea(null);\r
54                         //p.setChConv(null);\r
55                         \r
56                         CommonUtils.FieldCopy(p, this); // ディープコピーするよ\r
57                         \r
58                         p.pcenter = new ArrayList<ProgList>();\r
59                         \r
60                         /*\r
61                         // 地域設定をコピー\r
62                         p.aclist = new ArrayList<AreaCode>();\r
63                         for ( AreaCode ac : aclist ) {\r
64                                 p.aclist.add(ac.clone());\r
65                         }\r
66                         \r
67                         // 放送局設定をコピー\r
68                         p.crlist = new ArrayList<Center>();\r
69                         for ( Center cr : crlist ) {\r
70                                 p.crlist.add(cr.clone());\r
71                         }\r
72                         */\r
73 \r
74                         p.setSortedCRlist();\r
75 \r
76                         return p;\r
77                         \r
78                 } catch (CloneNotSupportedException e) {\r
79                         throw new InternalError(e.toString());\r
80                 }\r
81         }\r
82         \r
83         \r
84         /*******************************************************************************\r
85          * 共通情報\r
86          ******************************************************************************/\r
87         \r
88         /**\r
89          * Proxy\r
90          */\r
91         public static boolean setProxy(String host, String port) {\r
92                 Proxy newproxy = null;\r
93                 if ( host != null ) {\r
94                         try {\r
95                                 newproxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, Integer.valueOf(port)));\r
96                         }\r
97                         catch (Exception e) {\r
98                                 e.printStackTrace();\r
99                                 return false;\r
100                         }\r
101                 }\r
102                 proxy = newproxy;\r
103                 return true;\r
104         }\r
105         \r
106         private Proxy getProxy() { return proxy; }\r
107         \r
108         private static Proxy proxy = null;\r
109         \r
110         /**\r
111          * ChannelConvert.dat\r
112          */\r
113         public static void setChConv(ChannelConvert cc) { chconv = cc; }\r
114         \r
115         private ChannelConvert getChConv() { return chconv; }\r
116         \r
117         private static ChannelConvert chconv = null;\r
118         \r
119         /**\r
120          *  ログと進捗ダイアログ\r
121          */\r
122         public static void setProgressArea(StatusWindow o) { stw = o; }\r
123         \r
124         protected void reportProgress(String msg) {\r
125                 if (stw != null) {\r
126                         stw.append(msg);\r
127                 }\r
128                 System.out.println(msg);\r
129         }\r
130 \r
131         private static StatusWindow stw = null;\r
132         \r
133         \r
134         /*******************************************************************************\r
135          * オプション確認\r
136          ******************************************************************************/\r
137         \r
138         public boolean isAreaSelectSupported() { return true; }         // デフォルトはエリアを選べる\r
139         \r
140         \r
141         /*******************************************************************************\r
142          * 定数\r
143          ******************************************************************************/\r
144         \r
145         // コネクションタイムアウト\r
146         private static final int conntout = 5;\r
147         \r
148         // read()タイムアウト\r
149         private static final int readtout = 30;\r
150 \r
151         // 番組表が複数ページに跨っている場合、何ページまで追いかけるか\r
152         protected static final int REFPAGESMAX = 10;\r
153         \r
154         // 番組詳細に情報をconcatする時のセパレータ\r
155         protected final String DETAIL_SEP = "\n\n";\r
156         \r
157         // メッセージID\r
158         private static final String MSGID = "[番組表共通] ";\r
159         private static final String DBGID = "[DEBUG]"+MSGID;\r
160         private static final String ERRID = "[ERROR]"+MSGID;\r
161         \r
162         /*******************************************************************************\r
163          * メンバ変数関連\r
164          ******************************************************************************/\r
165         \r
166         // デバッグフラグ\r
167         public void setDebug(boolean b) { debug = b; }\r
168         protected boolean getDebug() { return debug; }  // 参照はサブクラスのみに許可\r
169         private boolean debug = false;\r
170         \r
171         // 通信エラー時のリトライ回数\r
172         protected void setRetryCount(int n) { retrycount = n; } // サブクラスのみに許可(特殊)\r
173         private int retrycount = 1;\r
174 \r
175         // User-Agent\r
176         public String getUserAgent() { return userAgent; }\r
177         public void setUserAgent(String s) { userAgent = s; }\r
178         private String userAgent = "";\r
179 \r
180         // 番組表キャッシュの格納フォルダ\r
181         public String getProgDir() { return progDir; }\r
182         public void setProgDir(String s) { progDir = s; }\r
183         private String progDir = "progcache";\r
184         \r
185         // 番組表キャッシュの有効時間(H)\r
186         public int getCacheExpired() { return cacheExpired; }\r
187         public void setCacheExpired(int h) { cacheExpired = h; }\r
188         private int cacheExpired = 6;\r
189         \r
190         // 番組詳細取得を行なうかどうか\r
191         public boolean getUseDetailCache() { return useDetailCache; }\r
192         public void setUseDetailCache(boolean b) { useDetailCache = b; }\r
193         private boolean useDetailCache = true;\r
194         \r
195         // 29時跨ぎで2つに分かれた番組情報をくっつけるかどうか\r
196         public boolean getContinueTomorrow() { return continueTomorrow; }\r
197         public void setContinueTomorrow(boolean b) { continueTomorrow = b; }\r
198         private boolean continueTomorrow = false;\r
199         \r
200         // タイトルから話数以降を分離する\r
201         public void setSplitEpno(boolean b) { splitEpno = b; }\r
202         public boolean isSplitEpno() { return splitEpno; }\r
203         private boolean splitEpno = true;\r
204         \r
205         // 可能なら8日分取得する\r
206         public boolean getExpandTo8() { return expandTo8; }\r
207         public void setExpandTo8(boolean b) { expandTo8 = b; }\r
208         private boolean expandTo8 = false;\r
209         \r
210         // 番組詳細キャッシュをオンライン取得するかどうか\r
211         @Deprecated\r
212         protected boolean isForceLoadDetInfo() { return forceLoadDetInfo; }\r
213         @Deprecated\r
214         private boolean forceLoadDetInfo = false;\r
215         \r
216         // もう使っていないようだ\r
217         //public void setAbnormal(boolean b) { abnormal = b; }\r
218         //public boolean getAbnormal() { return abnormal; }\r
219         //private boolean abnormal = false;\r
220 \r
221         \r
222         /*******************************************************************************\r
223          * 番組表固有の情報\r
224          ******************************************************************************/\r
225 \r
226         \r
227         /*******************************************************************************\r
228          * 番組情報\r
229          ******************************************************************************/\r
230         \r
231         /**\r
232          * <P>番組表が格納される場所\r
233          * <P>上から順の{@link ProgList}(pcenter/放送局別)->{@link ProgDateList}(pdate/日付別)→{@link ProgDetailList}pdetail/(番組詳細)\r
234          */\r
235         public ArrayList<ProgList> getCenters() { return(pcenter); }\r
236         public ArrayList<ProgList> pcenter = new ArrayList<ProgList>();\r
237         \r
238         \r
239         /*******************************************************************************\r
240          * 放送局情報\r
241          ******************************************************************************/\r
242         \r
243         /**\r
244          * 放送局名リスト\r
245          * @see ProgList#Center\r
246          * @see #sortedcrlist\r
247          */\r
248         public ArrayList<Center> getCRlist() { return(crlist); }\r
249         public ArrayList<Center> crlist = new ArrayList<Center>();\r
250         \r
251         /**\r
252          * ソート済み放送局名リスト\r
253          * @see ProgList#Center\r
254          * @see #crlist\r
255          */\r
256         public ArrayList<Center> getSortedCRlist() { return(sortedcrlist); }\r
257         public void setSortedCRlist() {\r
258                 sortedcrlist = new ArrayList<Center>();\r
259                 for (int order=1; order<=crlist.size(); order++) {\r
260                         for (Center center : crlist) {\r
261                                 if (center.getOrder() == order) {\r
262                                         sortedcrlist.add(center);\r
263                                         break;\r
264                                 }\r
265                         }\r
266                 }\r
267         }\r
268         public ArrayList<Center> sortedcrlist = new ArrayList<Center>();\r
269         \r
270         // 設定ファイルへ書き出し\r
271         public boolean saveCenter() {\r
272                 String centerListFile = getCenterListFile(getTVProgramId(), getSelectedCode());\r
273                 if ( ! CommonUtils.writeXML(centerListFile,crlist) ) {\r
274                         System.out.println("放送局リストの保存に失敗しました: "+centerListFile+", "+getSelectedCode());\r
275                         return false;\r
276                 }\r
277                 System.out.println("放送局リストを保存しました: "+centerListFile+", "+getSelectedCode());\r
278                 return true;\r
279         }\r
280         \r
281         // 放送局リストファイル名\r
282         protected String getCenterListFile(String id, String code) {\r
283                 return String.format("env%scenter.%s.%s.xml", File.separator, id, code);\r
284         }\r
285         \r
286         // 放送局名に一括してフィルタをかける\r
287         protected void attachChFilters() {\r
288                 for ( Center c : crlist ) {\r
289                         if ( c.getCenterOrig() == null ) {\r
290                                 c.setCenterOrig(c.getCenter());\r
291                         }\r
292                         c.setCenter(getChConv().get(c.getCenterOrig()));\r
293                 }\r
294         }\r
295         \r
296         // 放送局名個別にフィルタをかけたい\r
297         protected String getChName(String chorig) {\r
298                 return getChConv().get(chorig);\r
299         }\r
300 \r
301 \r
302         /*******************************************************************************\r
303          * 地域情報\r
304          ******************************************************************************/\r
305         \r
306         /**\r
307          * 地域コード のリストを取得する\r
308          */\r
309         public ArrayList<AreaCode> getAClist() { return(aclist); }\r
310         public ArrayList<AreaCode> aclist = new ArrayList<AreaCode>();\r
311         \r
312         /**\r
313          * 地域コードから地域名を取得\r
314          */\r
315         public String getArea(String code) {\r
316                 for (AreaCode ac : aclist) {\r
317                         if (ac.getCode().equals(code)) {\r
318                                 return(ac.getArea());\r
319                         }\r
320                 }\r
321                 return(null);\r
322         }\r
323 \r
324         /**\r
325          * 地域名から地域コードを取得\r
326          */\r
327         public String getCode(String area) {\r
328                 for (AreaCode ac : aclist) {\r
329                         if (ac.getArea().equals(area)) {\r
330                                 return(ac.getCode());\r
331                         }\r
332                 }\r
333                 return(null);\r
334         }\r
335         \r
336         /**\r
337          * 地域名を指定して選択中の地域を変更し、ついでにその地域コードを返す\r
338          * @see #getSelectedCode()\r
339          */\r
340         public String setSelectedAreaByName(String area) {\r
341                 AreaCode ac = null;\r
342                 for (AreaCode atmp : aclist) {\r
343                         if (atmp.getArea().equals(area)) {\r
344                         atmp.setSelected(true);\r
345                         ac = atmp;\r
346                         }\r
347                         else {\r
348                         atmp.setSelected(false);\r
349                         }\r
350                 }\r
351                 if (ac != null) {\r
352                         return(ac.getCode());\r
353                 }\r
354                 return(null);\r
355         }\r
356         \r
357         /**\r
358          * 地域コードを指定して選択中の地域を変更し、ついでにその地域名を返す\r
359          * @see #getSelectedArea()\r
360          */\r
361         public String setSelectedAreaByCode(String code) {\r
362                 AreaCode ac = null;\r
363                 for (AreaCode atmp : aclist) {\r
364                         if (atmp.getCode().equals(code)) {\r
365                         atmp.setSelected(true);\r
366                         ac = atmp;\r
367                         }\r
368                         else {\r
369                         atmp.setSelected(false);\r
370                         }\r
371                 }\r
372                 if (ac != null) {\r
373                         return(ac.getArea());\r
374                 }\r
375                 return(null);\r
376         }\r
377         \r
378         /**\r
379          * 選択中の地域名を返す\r
380          */\r
381         public String getSelectedArea() {\r
382                 for (AreaCode ac : aclist) {\r
383                         if (ac.getSelected() == true) {\r
384                                 return(ac.getArea());\r
385                         }\r
386                 }\r
387                 return(getDefaultArea());\r
388         }\r
389 \r
390         /**\r
391          * 選択中の地域コードを返す\r
392          */\r
393         public String getSelectedCode() {\r
394         for (AreaCode ac : aclist) {\r
395                 if (ac.getSelected() == true) {\r
396                         return(ac.getCode());\r
397                 }\r
398         }\r
399                 return(getCode(getDefaultArea()));\r
400         }\r
401         \r
402         // 設定ファイルへ書き出し\r
403         public void saveAreaCode() {\r
404                 if ( CommonUtils.writeXML(getAreaSelectedFile(), aclist) ) {\r
405                         System.out.println(MSGID+"地域リストを保存しました: "+getAreaSelectedFile());\r
406                 }\r
407                 else {\r
408                         System.err.println(ERRID+"地域リストの保存に失敗しました: "+getAreaSelectedFile());\r
409                 }\r
410         }\r
411         \r
412         // 地域情報ファイル名\r
413         protected String getAreaSelectedFile() {\r
414                 return String.format("env%sarea.%s.xml", File.separator, getTVProgramId());\r
415         }\r
416         \r
417         \r
418         /*******************************************************************************\r
419          * タイトル操作関連\r
420          ******************************************************************************/\r
421         \r
422         /**\r
423          * 話数重複排除\r
424          */\r
425         protected String doCutDupEpno(String title, String detail) {\r
426                 // タイトルの末尾に話数がついているかな?\r
427                 Matcher md = Pattern.compile("[  ]*?(#[#  -・0-9]+|第[0-9]+話)$").matcher(title);\r
428                 if ( ! md.find() ) {\r
429                         return title;\r
430                 }\r
431                 \r
432                 ArrayList<String> tnoa = new ArrayList<String>();\r
433                 {\r
434                         Matcher me = Pattern.compile("(\\d+)").matcher(md.group(1));\r
435                         while ( me.find() ) {\r
436                                 tnoa.add(me.group(1));\r
437                         }\r
438                         if ( tnoa.size() == 0 ) {\r
439                                 return title;\r
440                         }\r
441                 }\r
442                 \r
443                 // 番組詳細と重複しているかな?\r
444                 {\r
445                         ArrayList<String> dnoa = new ArrayList<String>();\r
446                         Matcher me = Pattern.compile("#[  ]*([0-9]+)|第([0-9]+)話").matcher(detail);\r
447                         while ( me.find() ) {\r
448                                 if ( me.group(1) != null ) {\r
449                                         dnoa.add(me.group(1));\r
450                                 }\r
451                                 else if ( me.group(2) != null ) {\r
452                                         dnoa.add(me.group(2));\r
453                                 }\r
454                         }\r
455                         if ( dnoa.size() == 0 ) {\r
456                                 return title;\r
457                         }\r
458                         \r
459                         for ( String tno : tnoa ) {\r
460                                 for ( String dno : dnoa ) {\r
461                                         if ( dno.equals(tno) ) {\r
462                                                 dnoa.remove(dno);\r
463                                                 break;\r
464                                         }\r
465                                 }\r
466                         }\r
467                         if ( dnoa.size() == 0 ) {\r
468                                 title = md.replaceFirst("");\r
469                         }\r
470                 }\r
471                 \r
472                 return title;\r
473         }\r
474         \r
475         /**\r
476          * サブタイトルの分離\r
477          * @param pdl\r
478          */\r
479         protected void doSplitSubtitle(ProgDetailList pdl)      {\r
480                 \r
481                 pdl.splitted_title = doCutDupEpno(pdl.title, pdl.detail);       // タイトルと番組詳細中の話数の重複排除\r
482                 \r
483                 String [] d = doSplitEpno(pdl.genre, pdl.splitted_title);       // 分離!\r
484                 \r
485                 pdl.splitted_title = pdl.title.substring(0,d[0].length());\r
486                 \r
487                 if ( d[1].length() > 0 ) {\r
488                         // 番組詳細はサブタイトル分離番組詳細へのポインタでいいよ\r
489                         pdl.splitted_detail = d[1]+DETAIL_SEP+pdl.detail;\r
490                         pdl.detail = pdl.splitted_detail.substring(d[1].length()+DETAIL_SEP.length());\r
491                 }\r
492                 else {\r
493                         // サブタイトルが分離されなかったから同じでいいよ\r
494                         pdl.splitted_detail = pdl.detail;\r
495                 }\r
496                 \r
497                 // タイトル&番組詳細のキーワード検索情報の設定\r
498                 String key_title;\r
499                 String key_detail;\r
500                 if (isSplitEpno()) {\r
501                         // サブタイトルを分離するならばそれを考慮した値を使う\r
502                         key_title = pdl.splitted_title;\r
503                         key_detail = pdl.splitted_detail;\r
504                 }\r
505                 else {\r
506                         key_title = pdl.title;\r
507                         key_detail = pdl.detail;\r
508                 }\r
509                 pdl.titlePop = TraceProgram.replacePop(key_title);\r
510                 pdl.detailPop = TraceProgram.replacePop(key_detail);\r
511                 \r
512                 pdl.SearchStrKeys = TraceProgram.splitKeys(pdl.titlePop);\r
513         }\r
514         \r
515         /**\r
516          * サブタイトル分離(Part.11 444-)\r
517          */\r
518         private String[] doSplitEpno(ProgGenre genre, String title) {\r
519                 if ( genre == ProgGenre.MOVIE || genre == ProgGenre.DOCUMENTARY ) {\r
520                         // 映画とドキュメンタリーは何もしない\r
521                 }\r
522                 else {\r
523                         if ( genre == ProgGenre.DORAMA ) {\r
524                                 // ドラマの場合は、"「*」"での分割をしない(土曜ドラマ「タイトル」とかあるため)\r
525                                 Matcher mc = Pattern.compile(spep_expr_dorama).matcher(title);\r
526                                 if ( mc.find() ) {\r
527                                         return(new String[] { mc.group(1),mc.group(2)+" " });\r
528                                 }\r
529                         }\r
530                         else {\r
531                                 // いきなり「で始まる場合や、タイトル中に『TOKYO「萌」探偵』のように「ほげほげ」を含む場合\r
532                                 Matcher mc = Pattern.compile("^([^  ]*「.+?」[^  ]+)(.*)$").matcher(title);\r
533                                 if ( mc.find() ) {\r
534                                         Matcher md = Pattern.compile("^[  ]*(.*?)[  ]*?"+spep_expr).matcher(mc.group(2));\r
535                                         if ( md.find() ) {\r
536                                                 if ( md.group(1).length() == 0 ) {\r
537                                                         return(new String[] { mc.group(1),md.group(2)+" " });\r
538                                                 }\r
539                                                 else {\r
540                                                         return(new String[] { mc.group(1)+" "+md.group(1),md.group(2)+" " });\r
541                                                 }\r
542                                         }\r
543                                         else {\r
544                                                 return(new String[] { title,"" });\r
545                                         }\r
546                                 }\r
547                                 // まあこれが普通\r
548                                 mc = Pattern.compile("^(.+?)[  ]*?"+spep_expr).matcher(title);\r
549                                 if ( mc.find() ) {\r
550                                         return(new String[] { mc.group(1),mc.group(2)+" " });\r
551                                 }\r
552                         }\r
553                 }\r
554                 return(new String[] { title,"" });\r
555         }\r
556         \r
557         // サブタイトル判定条件\r
558         private static final String spep_expr = "(([<<]?[((##♯第全「][第]?[12345678901234567890一二三四五六七八九十百千]+?[回話章]?|「).*)$";\r
559         \r
560         // サブタイトル判定条件(ジャンル=ドラマ専用)\r
561         private static final String spep_expr_dorama = "^(.+?)[  ]*?(([<<]?[((##♯第全「][第]?[12345678901234567890一二三四五六七八九十百千]+?[回話章]?).*)$";\r
562         \r
563         \r
564         /**\r
565          * NGワード\r
566          */\r
567         public void abon(ArrayList<String> ngword) {\r
568                 //\r
569                 if (ngword.size() == 0) {\r
570                         return;\r
571                 }\r
572                 for ( ProgList p : pcenter ) {\r
573                         for ( ProgDateList c : p.pdate ) {\r
574                                 for (ProgDetailList d : c.pdetail) {\r
575                                         for (String ngs : ngword) {\r
576                                                 if (d.title.indexOf(ngs) != -1 || d.detail.indexOf(ngs) != -1) {\r
577                                                         d.abon();\r
578                                                         break;\r
579                                                 }\r
580                                         }\r
581                                 }\r
582                         }\r
583                 }\r
584         }\r
585         \r
586         \r
587         \r
588         /*******************************************************************************\r
589          * 番組情報キャッシュ\r
590          ******************************************************************************/\r
591         \r
592         // キャッシュファイルが有効期限内か確認する\r
593         public boolean isCacheOld(String fname) {\r
594                 // キャッシュ期限が無期限の場合はDL禁止\r
595                 if (cacheExpired == 0) {\r
596                         return(false);\r
597                 }\r
598                 // キャッシュ期限があってファイルがない場合はDLしなさい\r
599                 //if (cacheExpired > 0 && fname == null) {\r
600                 if (fname == null) {    // あれだ、期限に関係なくファイル名の指定がなきゃDLするしかないよ\r
601                         return(true);\r
602                 }\r
603                 // 実際のファイルのタイムスタンプを確認する\r
604                 try {\r
605                         File f = new File(fname);\r
606                         if (f.exists() == true) {\r
607                                 long t = System.currentTimeMillis();\r
608                                 if (f.lastModified() < (t - cacheExpired*3600000L) ) {\r
609                                         // ファイルを更新\r
610                                         f.delete();\r
611                                         f.createNewFile();\r
612                                         return(true);\r
613                                 }\r
614                         }\r
615                         else {\r
616                                 // ファイルを作成        \r
617                                 f.createNewFile();\r
618                                 return(true);\r
619                         }\r
620                 }\r
621                 catch (Exception e) {\r
622                         // 例外\r
623                         System.out.println("Exception: isCacheOld() "+e);\r
624                 }\r
625                 return(false);\r
626         }\r
627         \r
628         \r
629         /*******************************************************************************\r
630          * 番組詳細キャッシュ(現在は使われていない)\r
631          ******************************************************************************/\r
632         \r
633         /**\r
634          * 番組詳細をオンライン取得するかどうか\r
635          */\r
636         @Deprecated\r
637         protected void chkForceLoadDetInfo(boolean force) {\r
638                 File f = new File(getDetCacheFile());\r
639                 if (force == true ||\r
640                                 (f.exists() == true && isCacheOld(getDetCacheFile()) == true) ||\r
641                                 (f.exists() == false && isCacheOld(null) == true)) {\r
642                         // Webからロードします\r
643                         forceLoadDetInfo = true;\r
644                 }\r
645                 else if (f.exists()) {\r
646                         // キャッシュファイルが有効なようなので利用します\r
647                         forceLoadDetInfo = false;\r
648                 }\r
649                 else {\r
650                         // 無くても平気…\r
651                         forceLoadDetInfo = false;\r
652                 }\r
653         }\r
654         \r
655         // 番組詳細キャッシュのLOAD\r
656         @Deprecated\r
657         protected HashMap<String,String> loadDetCache(){\r
658                 \r
659                 // 設定ファイルが存在していればファイルから\r
660                 File f = new File(getDetCacheFile());\r
661                 if (f.exists() == true) {\r
662                         @SuppressWarnings("unchecked")\r
663                         HashMap<String,String> cache = (HashMap<String, String>) CommonUtils.readXML(getDetCacheFile());\r
664                         if ( cache != null ) {\r
665                                 return cache;\r
666                         }\r
667                         \r
668                         System.out.println(ERRID+"【致命的エラー】番組詳細キャッシュが読み込めません: "+getDetCacheFile());\r
669                 }\r
670                 \r
671                 // キャッシュなし&エラーは空配列を返す\r
672                 return new HashMap<String, String>();\r
673         }\r
674         \r
675         // 番組詳細キャッシュのSAVE\r
676         @Deprecated\r
677         protected void saveDetCache(HashMap<String,String> cache) {\r
678                 if ( ! CommonUtils.writeXML(getDetCacheFile(), cache) ) {\r
679                         System.err.println(ERRID+"【致命的エラー】番組詳細キャッシュが書き込めません: "+getDetCacheFile());\r
680                 }\r
681         }\r
682         \r
683         \r
684         /*******************************************************************************\r
685          * 番組情報のリフレッシュ関連\r
686          ******************************************************************************/\r
687         \r
688         /**\r
689          * 日付変更線(29:00)をまたいだら過去のデータはカットする\r
690          * <B> PassedProgramでは使わない\r
691          */\r
692         public void refresh() {\r
693                 \r
694                 String critDate = CommonUtils.getDate529(0, true);\r
695                 for ( ProgList p : pcenter ) {\r
696                         int i = 0;\r
697                         for ( ProgDateList c : p.pdate ) {\r
698                                 if ( c.Date.compareTo(critDate) >= 0 ) {\r
699                                         break;\r
700                                 }\r
701                                 i++;\r
702                         }\r
703                         for ( int j=0; j<i; j++) {\r
704                                 p.pdate.remove(0);\r
705                         }\r
706                 }\r
707         }\r
708         \r
709         /**\r
710          * 24時間分番組枠が埋まっているかどうか確認する\r
711          */\r
712         public String chkComplete() {\r
713                 for ( ProgList p : pcenter ) {\r
714                         if (p.enabled) {\r
715                                 for ( ProgDateList c : p.pdate ) {\r
716                                         if (c.pdetail.size()<=1) {\r
717                                                 String msg = "番組枠が存在しません.("+p.Center+","+c.Date+")";\r
718                                                 System.out.println(msg);\r
719                                                 return(msg);\r
720                                         }\r
721                                         if (c.row < 24*60) {\r
722                                                 String msg = "番組枠が24時間分取得できませんでした.("+p.Center+","+c.Date+","+c.row+")";\r
723                                                 System.out.println(msg);\r
724                                                 return(msg);\r
725                                         }\r
726                                 }\r
727                                 if (p.pdate.size() < 7) {\r
728                                         String msg = "番組表が一週間分取得できませんでした.("+p.Center+")";\r
729                                         System.out.println(msg);\r
730                                         return(msg);\r
731                                 }\r
732                         }\r
733                 }\r
734                 return null;\r
735         }\r
736         \r
737         /**\r
738          * 開始終了日時の整理\r
739          */\r
740         public void setAccurateDate(ArrayList<ProgDateList> pcenter) {\r
741         \r
742                 // 先頭のエントリの開始時刻が 5:00 以前の場合\r
743                 for ( ProgDateList pcl : pcenter ) {\r
744                         if (pcl.pdetail.size() <= 0) {\r
745                                 continue;\r
746                         }\r
747                         \r
748                         ProgDetailList pd = pcl.pdetail.get(0);\r
749                         Matcher ma = Pattern.compile("(\\d\\d):(\\d\\d)").matcher(pd.start);\r
750                         if (ma.find()) {\r
751                                 int prelength = 0;\r
752                                 int ahh = Integer.valueOf(ma.group(1));\r
753                                 int amm = Integer.valueOf(ma.group(2));\r
754                                 \r
755                                 GregorianCalendar c = new GregorianCalendar();\r
756                                 c.setTime(new Date());\r
757                                 c.set(Calendar.HOUR_OF_DAY, ahh);\r
758                                 c.set(Calendar.MINUTE, amm);\r
759                                 \r
760                                 if ( pd.start.compareTo("05:00") < 0 ) {\r
761                                         // 5:00以前\r
762                                         prelength = (5*60+0)-(ahh*60+amm);\r
763                                         c.add(Calendar.MINUTE,prelength+pd.length);\r
764                                         pd.end = CommonUtils.getTime(c);\r
765                                 }\r
766                                 else if ( pd.start.compareTo("18:00") >= 0 && pd.start.compareTo("24:00") < 0 ) {\r
767                                         // 前日の24:00以前\r
768                                         prelength = (24*60+0)-(ahh*60+amm)+(5*60);\r
769                                         c.add(Calendar.MINUTE,prelength+pd.length);\r
770                                         pd.end = CommonUtils.getTime(c);\r
771                                 }\r
772                         }\r
773                 }\r
774 \r
775                 for ( ProgDateList pcl : pcenter ) {\r
776         \r
777                         GregorianCalendar c = CommonUtils.getCalendar(pcl.Date);\r
778         \r
779                         boolean extend = false;\r
780                         boolean overtwodays = false;\r
781                         for ( int i=0; i<pcl.pdetail.size(); i++ ) {\r
782                                 \r
783                                 ProgDetailList pdl = pcl.pdetail.get(i);\r
784                                 \r
785                                 // 番組情報がありません\r
786                                 if (pdl.start.compareTo("") == 0) {\r
787                                         continue;\r
788                                 }\r
789                                 \r
790                                 // 表示上の開始日時\r
791                                 if ( i == 0 ) {\r
792                                         if ( pdl.start.compareTo("18:00") >= 0 && pdl.start.compareTo("24:00") < 0 ) {\r
793                                                 // いったい何時間放送するんだよ(--#\r
794                                                 c.add(Calendar.DAY_OF_MONTH, -1);\r
795                                                 overtwodays = true;\r
796                                         }\r
797                                 }\r
798                                 else {\r
799                                         if ( (pdl.start.compareTo("00:00") >= 0 && pdl.start.compareTo("05:00") < 0 && pdl.end.compareTo("05:00") < 0) && extend == false) {\r
800                                                 c.add(Calendar.DAY_OF_MONTH, 1);\r
801                                                 extend = true;\r
802                                         }\r
803                                 }\r
804                                 pdl.startDateTime = String.format("%s %s", CommonUtils.getDate(c,false), pdl.start);\r
805         \r
806                                 // 正確な開始日\r
807                                 pdl.accurateDate = CommonUtils.getDate(c);\r
808                                 \r
809                                 // 表示上の終了日時\r
810                                 if ( overtwodays ) {\r
811                                         c.add(Calendar.DAY_OF_MONTH, 1);\r
812                                         overtwodays = false;\r
813                                 }\r
814                                 else {\r
815                                         if ( pdl.start.compareTo(pdl.end) > 0  && extend == false) {\r
816                                                 c.add(Calendar.DAY_OF_MONTH, 1);\r
817                                                 extend = true;\r
818                                         }\r
819                                 }\r
820                                 \r
821                                 pdl.endDateTime = String.format("%s %s", CommonUtils.getDate(c,false), pdl.end);\r
822                         }\r
823                 }\r
824                 \r
825                 // 29時をまたいで同タイトルが続いている場合は同一番組とみなす\r
826                 if ( continueTomorrow ) {\r
827                         for (int w=0; w<pcenter.size()-1; w++) {\r
828                                 if (pcenter.get(w).pdetail.size() > 0 && pcenter.get(w+1).pdetail.size() > 0) {\r
829                                         ProgDetailList pd1 = pcenter.get(w).pdetail.get(pcenter.get(w).pdetail.size()-1);\r
830                                         ProgDetailList pd2 = pcenter.get(w+1).pdetail.get(0);\r
831                                         if (pd1.title.equals(pd2.title)) {\r
832                                                 pd1.end = pd2.end;\r
833                                                 pd1.endDateTime = pd2.endDateTime;\r
834                                                 \r
835                                                 pd2.start = pd1.start;\r
836                                                 pd2.startDateTime = pd1.startDateTime;\r
837                                                 pd2.accurateDate = pd1.accurateDate;\r
838                                         }\r
839                                         else if (pd2.title.equals("承前")) {\r
840                                                 pd1.end = pd2.end;\r
841                                                 pd1.endDateTime = pd2.endDateTime;\r
842                                                 \r
843                                                 pd2.start = pd1.start;\r
844                                                 pd2.startDateTime = pd1.startDateTime;\r
845                                                 pd2.accurateDate = pd1.accurateDate;\r
846                                                 \r
847                                                 pd2.title = pd1.title;\r
848                                                 pd2.detail = pd1.detail;\r
849                                                 pd2.setAddedDetail(pd1.getAddedDetail());\r
850                                                 pd2.link = pd1.link;\r
851                                                 pd2.titlePop = pd1.titlePop;\r
852                                                 pd2.detailPop = pd1.detailPop;\r
853                                                 pd2.SearchStrKeys = pd1.SearchStrKeys;\r
854                                                 pd2.nosyobo = pd1.nosyobo;\r
855                                                 pd2.extension = pd1.extension;\r
856                                                 pd2.flag = pd1.flag;\r
857                                                 pd2.genre = pd1.genre;\r
858                                         }\r
859                                 }\r
860                         }\r
861                 }\r
862         }\r
863         \r
864         // 以前に取得したデータから当日の取得不能領域のデータを補完する\r
865         protected void CompensatesPrograms(ArrayList<ProgList> newplist) {\r
866                 //\r
867                 for ( ProgList newpl : newplist ) {\r
868                         \r
869                         if ( newpl.enabled != true ) {\r
870                                 // 無効局は処理なし\r
871                                 continue;\r
872                         }\r
873                         \r
874                         ArrayList<ProgDateList> newpcla = newpl.pdate; \r
875                         if ( newpcla.size() == 0 ) {\r
876                                 // 日付リストが存在しない場合は処理なし\r
877                                 continue;\r
878                         }\r
879                         ProgDateList newpcl = newpcla.get(0);\r
880                         \r
881                         ArrayList<ProgDetailList> newpdla = newpcl.pdetail;\r
882                         if ( newpdla.size() == 0 ) {\r
883                                 // 番組情報が存在しない場合は処理なし\r
884                                 if (debug) System.out.println(DBGID+"番組表情報がないので過去ログは参照しない: "+newpcl.Date+" "+newpl.Center);\r
885                                 continue;\r
886                         }\r
887                         \r
888                         if ( newpdla.get(0).start.length() != 0 ) {\r
889                                 // 先頭の番組情報が「番組情報がありません」以外の場合は処理なし\r
890                                 if (debug) System.out.println(DBGID+"先頭から有効な情報なので過去ログは参照しない: "+newpcl.Date+" "+newpl.Center+" "+newpdla.get(0).start+" "+newpdla.get(0).title);\r
891                                 continue;\r
892                         }\r
893                         \r
894                         PassedProgram oldplist = new PassedProgram();\r
895                         if ( ! oldplist.loadByCenter(newpcl.Date, newpl.Center) || oldplist.getProgCount() == 0 ) {\r
896                                 // 過去情報が取得できなければ処理なし\r
897                                 System.out.println(DBGID+"過去ログに情報はありませんでした");\r
898                                 continue;\r
899                         }\r
900                         ProgDateList oldpcl = oldplist.pcenter.get(0).pdate.get(0);\r
901                         \r
902                         // 補填候補抽出\r
903                         ArrayList<ProgDetailList> tmppdla = new ArrayList<ProgDetailList>();\r
904                         if ( newpdla.size() == 1 ) {\r
905                                 // 「番組情報がありません」しかない場合は全面複写\r
906                                 for ( ProgDetailList oldpdl : oldpcl.pdetail ) {\r
907                                         tmppdla.add(oldpdl.clone());\r
908                                 }\r
909                         }\r
910                         else {\r
911                                 int idx = 0;\r
912                                 for ( ProgDetailList oldpdl : oldpcl.pdetail ) {\r
913                                         if ( idx == 0 ) {\r
914                                                 // 過去ログの最初は無条件に追加してよい\r
915                                                 tmppdla.add(oldpdl.clone());\r
916                                         }\r
917                                         else if ( oldpdl.startDateTime.compareTo(newpdla.get(1).startDateTime) < 0 ) {\r
918                                                 // 2個目以降は当日の有効情報の前まで(「番組情報がありません」は無条件追加)\r
919                                                 tmppdla.add(oldpdl.clone());\r
920                                         }\r
921                                         else {\r
922                                                 // 有効情報を越えたら終了\r
923                                                 break;\r
924                                         }\r
925                                         idx++;\r
926                                 }\r
927                         }\r
928                         \r
929                         // 先頭の「番組情報はありません」と差し替えて補填\r
930                         newpdla.remove(0);\r
931                         for ( int i=0; i<tmppdla.size(); i++ ) {\r
932                                 newpdla.add(i,tmppdla.get(i));\r
933                         }\r
934                         tmppdla = null;\r
935                 }\r
936         }\r
937 \r
938         \r
939         /*******************************************************************************\r
940          * フラグ処理関連\r
941          ******************************************************************************/\r
942         \r
943         /**\r
944          * 延長警告を設定する\r
945          */\r
946         public void setExtension(String spoexSearchStart, String spoexSearchEnd, boolean spoexLimitation, ArrayList<SearchKey> extKeys) {\r
947                 //\r
948                 for ( ProgList p : pcenter ) {\r
949                         // 局ごと\r
950                         \r
951                         // キーワード検索用\r
952                         String centerPop = TraceProgram.replacePop(p.Center);\r
953                         \r
954                         for ( ProgDateList c : p.pdate ) {\r
955                                 // 日ごと\r
956                                 boolean poisoned = false;\r
957                                 for (ProgDetailList d : c.pdetail) {\r
958                                         // 番組ごと\r
959                                         boolean soi = false;\r
960                                         for ( SearchKey k : extKeys ) {\r
961                                                 // 個別設定による延長可否\r
962                                                 boolean isMatch = SearchProgram.isMatchKeyword(k, ((k.getCaseSensitive()==false)?(centerPop):(p.Center)), d);\r
963                                                 if (isMatch) {\r
964                                                         if (k.getInfection().equals("0")) {\r
965                                                                 // 延長感染源にする\r
966                                                                 soi = true;\r
967                                                         }\r
968                                                         else {\r
969                                                                 // 延長感染源にしない(優先)\r
970                                                                 soi = false;\r
971                                                                 break;\r
972                                                         }\r
973                                                 }\r
974                                         }\r
975                                         if (soi) {\r
976                                                 poisoned = true;\r
977                                         }\r
978                                         \r
979                                         d.extension = poisoned;\r
980                                 }\r
981                         }\r
982                 }\r
983         }\r
984         \r
985         /*\r
986          * タイトルからフラグを抽出する\r
987          */\r
988         protected void doSplitFlags(ProgDetailList pdl, HashMap<String, String> nf) {\r
989                 \r
990                 Matcher md = Pattern.compile("(#1|第1話)\\b").matcher(pdl.title);\r
991                 if ( md.find() ) {\r
992                         pdl.flag = ProgFlags.NEW;\r
993                 }\r
994                 \r
995                 md = Pattern.compile("([  ]?[<<]新[>>]| 新$| NEW$)").matcher(pdl.title);\r
996                 if ( md.find() ) {\r
997                         pdl.flag = ProgFlags.NEW;\r
998                         pdl.title = md.replaceAll("");\r
999                 }\r
1000                 md = Pattern.compile("([  ]?[<<]終[>>]| 終$| END$)").matcher(pdl.title);\r
1001                 if ( md.find() ) {\r
1002                         pdl.flag = ProgFlags.LAST;\r
1003                         pdl.title = md.replaceAll("");  \r
1004                 }\r
1005                 md = Pattern.compile("[((]終[))]",Pattern.DOTALL).matcher(pdl.detail);\r
1006                 if ( md.find() ) {\r
1007                         pdl.flag = ProgFlags.LAST;\r
1008                 }\r
1009                 md = Pattern.compile("^無料≫").matcher(pdl.title);\r
1010                 if ( md.find() ) {\r
1011                         pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
1012                         pdl.title = md.replaceAll("");  \r
1013                 }\r
1014                 \r
1015                 Pattern pat = Pattern.compile("初放送",Pattern.DOTALL);\r
1016                 if ( pat.matcher(pdl.detail).find() ) {\r
1017                         pdl.addOption(ProgOption.FIRST);\r
1018                 }\r
1019                 else if ( pat.matcher(pdl.title).find() ) {\r
1020                         pdl.addOption(ProgOption.FIRST);\r
1021                 }\r
1022 \r
1023                 pat = Pattern.compile("(視聴(..)?制限|[RR]([--]?[11][5588][++]?|指定))",Pattern.DOTALL);\r
1024                 if ( pat.matcher(pdl.detail).find() ) {\r
1025                         pdl.addOption(ProgOption.RATING);\r
1026                 }\r
1027                 else if ( pat.matcher(pdl.title).find() ) {\r
1028                         pdl.addOption(ProgOption.RATING);\r
1029                 }\r
1030                 \r
1031                 if ( pdl.detail.indexOf("5.1サラウンド") != -1 ) {\r
1032                         pdl.addOption(ProgOption.SURROUND);\r
1033                 }\r
1034 \r
1035                 HashMap<String, String> xf = new HashMap<String, String>();\r
1036                 \r
1037                 String flagExpr = "[\\[[((【](.{1,3})[\\]]))】]";\r
1038                 Matcher mx = Pattern.compile(flagExpr).matcher(pdl.title);\r
1039                 while (mx.find()) {\r
1040                         if (mx.group(1).equals("新") || mx.group(1).equals("新番組")) {\r
1041                                 pdl.flag = ProgFlags.NEW;\r
1042                         }\r
1043                         else if (mx.group(1).equals("終") || mx.group(1).equals("完") || mx.group(1).equals("最終回")) {\r
1044                                 pdl.flag = ProgFlags.LAST;\r
1045                         }\r
1046                         else if (mx.group(1).equals("再")) {\r
1047                                 pdl.addOption(ProgOption.REPEAT);\r
1048                         }\r
1049                         else if (mx.group(1).equals("初")) {\r
1050                                 pdl.addOption(ProgOption.FIRST);\r
1051                         }\r
1052                         else if (mx.group(1).equals("生")) {\r
1053                                 pdl.addOption(ProgOption.LIVE);\r
1054                         }\r
1055                         \r
1056                         else if (mx.group(1).equals("二/吹")) {\r
1057                                 pdl.addOption(ProgOption.BILINGUAL);\r
1058                                 pdl.addOption(ProgOption.STANDIN);\r
1059                         }\r
1060                         else if (mx.group(1).equals("字") || mx.group(1).equals("字幕") || mx.group(1).equals("字幕版")) {\r
1061                                 pdl.addOption(ProgOption.SUBTITLE);\r
1062                         }\r
1063                         else if (mx.group(1).equals("二")) {\r
1064                                 pdl.addOption(ProgOption.BILINGUAL);\r
1065                         }\r
1066                         else if (mx.group(1).equals("多")) {\r
1067                                 pdl.addOption(ProgOption.MULTIVOICE);\r
1068                         }\r
1069                         else if (mx.group(1).equals("SS") || mx.group(1).equals("5.1")) {\r
1070                                 pdl.addOption(ProgOption.SURROUND);\r
1071                         }\r
1072                         else if (mx.group(1).equals("吹") || mx.group(1).equals("吹替") || mx.group(1).equals("吹替版")) {\r
1073                                 pdl.addOption(ProgOption.STANDIN);      // (ないよ)\r
1074                         }\r
1075                         else if (mx.group(1).equals("デ")) {\r
1076                                 pdl.addOption(ProgOption.DATA);\r
1077                         }\r
1078                         else if (mx.group(1).equals("無") || mx.group(1).equals("無料")) {\r
1079                                 //pdl.addOption(ProgOption.NOSCRUMBLE);\r
1080                                 pdl.noscrumble = ProgScrumble.NOSCRUMBLE;\r
1081                         }\r
1082                         \r
1083                         else if (mx.group(1).matches("^(S|N|B|映|双|解|手|天|英|日|録|HV)$")) {\r
1084                                 // 無視するフラグ\r
1085                                 if ( mx.group(1).equals("日") && ( ! pdl.title.matches(String.format("^(%s)*[((]日[))].*", flagExpr)) && ! pdl.title.matches(".*[\\[[]日[\\]]].*")) ) {\r
1086                                         // 削除しないフラグ(特殊)\r
1087                                         continue;\r
1088                                 }\r
1089                         }\r
1090                         else if (mx.group(1).matches("^(韓|仮|[前後][編篇半]|[月火水木金土]|[0-90-9]+)$")) {\r
1091                                 // 削除しないフラグ\r
1092                                 continue;\r
1093                         }\r
1094                         \r
1095                         else {\r
1096                                 // 未知のフラグ\r
1097                                 nf.put(mx.group(1),null);\r
1098                                 continue;\r
1099                         }\r
1100                         \r
1101                         // 削除するフラグ\r
1102                         xf.put(mx.group(1), null);\r
1103                 }\r
1104                 {\r
1105                         // 認識されたフラグだけ削除する.\r
1106                         String repExpr = "";\r
1107                         for ( String f : xf.keySet() ) {\r
1108                                 repExpr += String.format("%s|",f);\r
1109                         }\r
1110                         if ( repExpr.length() > 0 ) {\r
1111                                 repExpr = "[\\[[((【]("+repExpr.substring(0, repExpr.length()-1)+")[\\]]))】]";\r
1112                                 pdl.title = pdl.title.replaceAll(repExpr, "");\r
1113                         }\r
1114                 }\r
1115                 \r
1116                 if ( pdl.title.matches("^特[::].*") ) {\r
1117                         pdl.option.add(ProgOption.SPECIAL);\r
1118                         pdl.title = pdl.title.substring(2);\r
1119                 }\r
1120                 else if ( pdl.detail.contains("OVA") && ! pdl.detail.contains("+OVA") ) {\r
1121                         pdl.option.add(ProgOption.SPECIAL);\r
1122                 }\r
1123                 else if ( pdl.detail.contains("未放送") ) {\r
1124                         pdl.option.add(ProgOption.SPECIAL);\r
1125                 }\r
1126         }\r
1127         \r
1128         /**\r
1129          * マルチジャンル処理\r
1130          */\r
1131         protected void setMultiGenre(ProgDetailList pdl, ArrayList<String> genrelist) {\r
1132                 // コード順にならべかえる\r
1133                 Collections.sort(genrelist);\r
1134 \r
1135                 // ここに入ってこない限り genrelist == null なので対応プラグイン以外ではマルチジャンルは機能しない\r
1136                 pdl.genrelist = new ArrayList<TVProgram.ProgGenre>();\r
1137                 pdl.subgenrelist = new ArrayList<TVProgram.ProgSubgenre>(); \r
1138                                 \r
1139                 String gcode = ProgGenre.NOGENRE.toIEPG();\r
1140                 String subgcode = ProgSubgenre.NOGENRE_ETC.toIEPG();\r
1141                 \r
1142                 // マルチジャンルコードを設定する\r
1143                 for ( String gstr : genrelist ) {\r
1144                         // ジャンルコードが複数ある場合は基本的に最初のものを代表にするが、一部例外をもうける(ニュースよりドキュメンタリー優先、など)\r
1145                         // 鯛ナビの一覧で表示されるジャンルは代表のものだけである\r
1146                         String gv = gstr.substring(0,1); \r
1147                         String subgv = gstr.substring(1,2); \r
1148                         if ( gcode.equals(ProgGenre.NOGENRE.toIEPG()) ) {\r
1149                                 gcode = gv;\r
1150                                 subgcode = subgv;\r
1151                         }\r
1152                         else if ( gcode.equals(ProgGenre.NEWS.toIEPG()) && gv.equals(ProgGenre.DOCUMENTARY.toIEPG())) {\r
1153                                 gcode = gv;\r
1154                                 subgcode = subgv;\r
1155                         }\r
1156                         /*\r
1157                         else if ( gcode.equals(ProgGenre.MUSIC.toIEPG()) && md.group(1).equals(ProgGenre.VARIETY.toIEPG())) {\r
1158                                 gcode = md.group(1);\r
1159                                 subgcode = md.group(2);\r
1160                         }\r
1161                         */\r
1162                         \r
1163                         // 3.14.12βでマルチジャンル対応を追加した\r
1164                         // 一覧では代表しか見えないが、検索処理ではすべてのジャンルコードが対象になる\r
1165                         {\r
1166                                 ProgGenre ng = ProgGenre.NOGENRE;\r
1167                                 ProgSubgenre nsubg = ProgSubgenre.NOGENRE_ETC;\r
1168                                 for ( ProgGenre g : ProgGenre.values() ) {\r
1169                                         if ( g.toIEPG().equals(gv) ) {\r
1170                                                 ng = g;\r
1171                                                 for ( ProgSubgenre subg : ProgSubgenre.values() ) {\r
1172                                                         if ( subg.getGenre().equals(g) && subg.toIEPG().equals(subgv) ) {\r
1173                                                                 nsubg = subg;\r
1174                                                                 break;\r
1175                                                         }\r
1176                                                 }\r
1177                                                 break;\r
1178                                         }\r
1179                                 }\r
1180                                 if ( ng != ProgGenre.NOGENRE ) {\r
1181                                         pdl.genrelist.add(ng);\r
1182                                         pdl.subgenrelist.add(nsubg);\r
1183                                 }\r
1184                         }\r
1185                         if ( pdl.genrelist.size() == 0 ) {\r
1186                                 pdl.genrelist.add(ProgGenre.NOGENRE);\r
1187                                 pdl.subgenrelist.add(ProgSubgenre.NOGENRE_ETC);\r
1188                         }\r
1189                 }\r
1190                 \r
1191                 // 代表ジャンルコードを設定する\r
1192                 for ( ProgGenre g : ProgGenre.values() ) {\r
1193                         if ( g.toIEPG().equals(gcode) ) {\r
1194                                 pdl.genre = g;\r
1195                                 for ( ProgSubgenre subg : ProgSubgenre.values() ) {\r
1196                                         if ( subg.getGenre().equals(g) && subg.toIEPG().equals(subgcode) ) {\r
1197                                                 pdl.subgenre = subg;\r
1198                                                 break;\r
1199                                         }\r
1200                                 }\r
1201                                 break;\r
1202                         }\r
1203                 }\r
1204         }\r
1205         \r
1206         \r
1207         /*******************************************************************************\r
1208          * 通信系\r
1209          ******************************************************************************/\r
1210         \r
1211         // Web上から取得してファイルにキャッシュする\r
1212         \r
1213         // GET型\r
1214         public void webToFile(String uri, String fname, String thisEncoding) {\r
1215                 webToFile(uri, null, null, null, fname, thisEncoding);\r
1216         }\r
1217         \r
1218         // POST型\r
1219         public void webToFile(String uri, String pstr, String cookie, String referer, String fname, String thisEncoding) {\r
1220                 int retry = 0;\r
1221                 while (true) {\r
1222                         if ( _webToFile(uri, pstr, cookie, referer, fname, thisEncoding) == true ) {\r
1223                                 break;\r
1224                         }\r
1225                         if ( ++retry > retrycount ) {\r
1226                                 break;\r
1227                         }\r
1228                         System.out.println("wait for retry...");\r
1229                         CommonUtils.milSleep(1000);\r
1230                 }\r
1231         }\r
1232         \r
1233         // GET/POST本体\r
1234         private boolean _webToFile(String uri, String pstr, String cookie, String referer, String fname, String thisEncoding) {\r
1235                 \r
1236                 HttpURLConnection ucon = null;\r
1237                 BufferedWriter filewriter = null;\r
1238                 BufferedReader filereader = null;\r
1239                 BufferedOutputStream streamwriter = null;\r
1240                 BufferedInputStream streamreader = null;\r
1241                 try {\r
1242                         ucon = _webToXXX(uri,pstr,cookie,referer,thisEncoding);\r
1243                         if (ucon == null) {\r
1244                                 return false;\r
1245                         }\r
1246                         \r
1247                         // 一時ファイルに書き出し\r
1248                         if (thisEncoding != null) {\r
1249                                 filewriter = new BufferedWriter(new FileWriter(fname+".tmp"));\r
1250                                 filereader = new BufferedReader(new InputStreamReader(ucon.getInputStream(), thisEncoding));\r
1251                                 String str;\r
1252                             while((str = filereader.readLine()) != null){\r
1253                                 filewriter.write(str);\r
1254                                 filewriter.write("\n");\r
1255                             }\r
1256                             filewriter.close();\r
1257                             filewriter = null;\r
1258                             filereader.close();\r
1259                             filereader = null;\r
1260                         }\r
1261                         else {\r
1262                                 streamwriter = new BufferedOutputStream(new FileOutputStream(fname+".tmp"));\r
1263                                 streamreader = new BufferedInputStream(ucon.getInputStream());\r
1264                                 byte[] buf = new byte[65536];\r
1265                                 int len;\r
1266                             while((len = streamreader.read(buf,0,buf.length)) != -1){\r
1267                                 streamwriter.write(buf,0,len);\r
1268                             }\r
1269                             streamwriter.close();\r
1270                             streamwriter = null;\r
1271                             streamreader.close();\r
1272                             streamreader = null;\r
1273                         }\r
1274                     ucon.disconnect();\r
1275                     ucon = null;\r
1276                         \r
1277                     // クローズしてからじゃないと失敗するよ \r
1278                     \r
1279                         // キャッシュファイルに変換\r
1280                     File o = new File(fname);\r
1281                     if ( o.exists() && ! o.delete() ) {\r
1282                         System.err.println("削除できないよ: "+fname);\r
1283                     }\r
1284                     File n = new File(fname+".tmp");\r
1285                     if ( ! n.renameTo(o) ) {\r
1286                         System.err.println("リネームできないよ: "+fname+".tmp to "+fname);\r
1287                         return false;\r
1288                     }\r
1289                         \r
1290                     return true;\r
1291                 }\r
1292                 catch (Exception e) {\r
1293                         // 例外\r
1294                         System.out.println("Webアクセスに失敗しました("+uri+"): "+e);\r
1295                 }\r
1296                 finally {\r
1297                         CommonUtils.closing(filewriter);\r
1298                         CommonUtils.closing(filereader);\r
1299                         CommonUtils.closing(streamwriter);\r
1300                         CommonUtils.closing(streamreader);\r
1301                         CommonUtils.closing(ucon);\r
1302                 }\r
1303                 return false;\r
1304         }\r
1305 \r
1306         // Web上から取得してバッファに格納する\r
1307         \r
1308         // GET型\r
1309         public String webToBuffer(String uri, String thisEncoding, boolean nocr) {\r
1310                 return webToBuffer(uri, null, null, null, thisEncoding, nocr);\r
1311         }\r
1312         \r
1313         // POST型\r
1314         public String webToBuffer(String uri, String pstr, String cookie, String referer, String thisEncoding, boolean nocr) {\r
1315                 int retry = 0;\r
1316                 while (true) {\r
1317                         String response = _webToBuffer(uri, pstr, cookie, referer, thisEncoding, nocr); \r
1318                         if ( response != null ) {\r
1319                                 return response;\r
1320                         }\r
1321                         if ( ++retry > retrycount ) {\r
1322                                 break;\r
1323                         }\r
1324                         System.out.println("wait for retry...");\r
1325                         CommonUtils.milSleep(1000);\r
1326                 }\r
1327                 return null;\r
1328         }\r
1329         \r
1330         // 本体\r
1331         private String _webToBuffer(String uri, String pstr, String cookie, String referer, String thisEncoding, boolean nocr) {\r
1332 \r
1333                 if ( thisEncoding == null ) {\r
1334                         return null;\r
1335                 }\r
1336                 \r
1337                 try {\r
1338                         HttpURLConnection ucon = null;\r
1339                         BufferedReader reader = null;\r
1340                         try {\r
1341                                 ucon = _webToXXX(uri,pstr,cookie,referer,thisEncoding);\r
1342                                 if (ucon == null) {\r
1343                                         return null;\r
1344                                 }\r
1345                                 \r
1346                                 // バッファ作成\r
1347                                 StringBuilder sb = new StringBuilder();\r
1348                                 reader = new BufferedReader(new InputStreamReader(ucon.getInputStream(), thisEncoding));\r
1349                                 String str;\r
1350                             while((str = reader.readLine()) != null){\r
1351                                 sb.append(str);\r
1352                                 if ( ! nocr) sb.append("\n");\r
1353                             }\r
1354                             return sb.toString();\r
1355                         }\r
1356                         catch (Exception e) {\r
1357                                 System.out.println("Webアクセスに失敗しました("+uri+"): "+e.toString());\r
1358                                 //e.printStackTrace();\r
1359                         }\r
1360                         finally {\r
1361                                 if ( reader != null ) {\r
1362                                         reader.close();\r
1363                                         reader = null;\r
1364                                 }\r
1365                                 if ( ucon != null ) {\r
1366                                         ucon.disconnect();\r
1367                                         ucon = null;\r
1368                                 }\r
1369                         }\r
1370                 }\r
1371                 catch ( Exception e ) {\r
1372                         // close()の例外は無視\r
1373                 }\r
1374                 return null;\r
1375         }\r
1376         \r
1377         /*\r
1378          * File/Bufferの共通部品\r
1379          */\r
1380         \r
1381         private HttpURLConnection _webToXXX(String uri, String pstr, String cookie, String referer, String thisEncoding) {\r
1382                 try {\r
1383                         URL url = new URL(uri);\r
1384                         HttpURLConnection ucon;\r
1385                         if ( getProxy() == null ) {\r
1386                                 ucon = (HttpURLConnection)url.openConnection();\r
1387                         }\r
1388                         else {\r
1389                                 ucon = (HttpURLConnection)url.openConnection(getProxy());\r
1390                         }\r
1391                         ucon.setConnectTimeout(conntout*1000);\r
1392                         ucon.setReadTimeout(readtout*1000);\r
1393         \r
1394                         ucon.addRequestProperty("User-Agent", getUserAgent());\r
1395                         \r
1396                         if ( referer != null ) {\r
1397                                 ucon.addRequestProperty("Referer", referer);\r
1398                         }\r
1399                         \r
1400                         if ( cookie != null ) {\r
1401                                 ucon.addRequestProperty("Cookie", cookie);\r
1402                         }\r
1403                         \r
1404                         // Cookie処理(CookieManagerはなぜうまく動かないんだ…orz)\r
1405                         if ( cookie_cache.size() > 0 ) {\r
1406                                 String cStr = "";\r
1407                                 for ( String key : cookie_cache.keySet() ) {\r
1408                                         cStr += key+"="+cookie_cache.get(key)+"; ";\r
1409                                 }\r
1410                                 ucon.addRequestProperty("Cookie", cStr);\r
1411                         }\r
1412         \r
1413                         if (pstr != null) {\r
1414                                 ucon.setRequestMethod("POST");\r
1415                                 ucon.setDoOutput(true);\r
1416                                 ucon.setDoInput(true);\r
1417                         }\r
1418                         else {\r
1419                                 ucon.setRequestMethod("GET");\r
1420                                 ucon.connect();\r
1421                         }\r
1422 \r
1423                         // POSTの場合は別途データを送る\r
1424                         if (pstr != null && thisEncoding != null) {\r
1425                                 OutputStream writer = ucon.getOutputStream();\r
1426                                 writer.write(pstr.getBytes(thisEncoding));\r
1427                                 writer.flush();\r
1428                                 writer.close();\r
1429                         }\r
1430                         \r
1431                         // Cookie処理\r
1432                         if ( uri.matches(".*dimora.jp.*") || uri.matches(".*\\.yahoo\\.co\\.jp.*") ) {\r
1433                                 List<String> hf = ucon.getHeaderFields().get("Set-Cookie");\r
1434                                 if ( hf != null ) {\r
1435                                 for ( String rcookie :  hf ) {\r
1436                                         String[] rc1 = rcookie.split(";",2);\r
1437                                         String[] rc2 = rc1[0].split("=",2);\r
1438                                         cookie_cache.put(rc2[0], rc2[1]);\r
1439                                 }\r
1440                                 }\r
1441                     }\r
1442                         \r
1443                         return ucon;\r
1444                         \r
1445                 } catch (MalformedURLException e) {\r
1446                         e.printStackTrace();\r
1447                 } catch (IOException e) {\r
1448                         e.printStackTrace();\r
1449                 }\r
1450                 \r
1451                 return null;\r
1452         }\r
1453         \r
1454         protected String getCookie(String key) {\r
1455                 return cookie_cache.get(key);\r
1456         }\r
1457         protected void addCookie(String key, String val) {\r
1458                 cookie_cache.put(key,val);\r
1459         }\r
1460         protected void delCookie(String key) {\r
1461                 cookie_cache.remove(key);\r
1462         }\r
1463         protected void clrCookie() {\r
1464                 cookie_cache.clear();\r
1465         }\r
1466         \r
1467         // Cookieの情報をキャッシュするよ\r
1468         private HashMap<String,String> cookie_cache = new HashMap<String, String>();\r
1469                 \r
1470         \r
1471         /*******************************************************************************\r
1472          * ここから下は該当機能が無効なプラグイン用のダミー\r
1473          ******************************************************************************/\r
1474         \r
1475         public String getTVProgramId() { return "DUMMY"; }\r
1476         \r
1477         public String getDefaultArea() { return "東京"; }\r
1478         \r
1479         // 番組詳細キャッシュファイル名\r
1480         protected String getDetCacheFile() { return ""; }\r
1481         \r
1482         // 番組詳細をattachする\r
1483         protected void attachDetails(ArrayList<ProgList> plist, HashMap<String,String> oldcache, HashMap<String,String> newcache) {\r
1484                 // ダミー\r
1485         }\r
1486 \r
1487         // フリーテキストによるオプション指定\r
1488         public boolean setOptString(String s) { return true; }  // ダミー\r
1489         public String getOptString() { return null; }                   // ダミー\r
1490         \r
1491 }\r