OSDN Git Service

@Override追加
[jindolf/Jindolf.git] / src / main / java / jp / sourceforge / jindolf / AccountPanel.java
1 /*\r
2  * Account panel\r
3  *\r
4  * License : The MIT License\r
5  * Copyright(c) 2008 olyutorskii\r
6  */\r
7 \r
8 package jp.sourceforge.jindolf;\r
9 \r
10 import java.awt.BorderLayout;\r
11 import java.awt.Container;\r
12 import java.awt.Frame;\r
13 import java.awt.GridBagConstraints;\r
14 import java.awt.GridBagLayout;\r
15 import java.awt.Insets;\r
16 import java.awt.event.ActionEvent;\r
17 import java.awt.event.ActionListener;\r
18 import java.awt.event.ItemEvent;\r
19 import java.awt.event.ItemListener;\r
20 import java.io.IOException;\r
21 import java.util.HashMap;\r
22 import java.util.Map;\r
23 import javax.swing.BorderFactory;\r
24 import javax.swing.JButton;\r
25 import javax.swing.JComboBox;\r
26 import javax.swing.JComponent;\r
27 import javax.swing.JDialog;\r
28 import javax.swing.JLabel;\r
29 import javax.swing.JOptionPane;\r
30 import javax.swing.JPanel;\r
31 import javax.swing.JPasswordField;\r
32 import javax.swing.JSeparator;\r
33 import javax.swing.JTextArea;\r
34 import javax.swing.JTextField;\r
35 import javax.swing.border.Border;\r
36 import jp.sourceforge.jindolf.corelib.LandState;\r
37 \r
38 /**\r
39  * ログインパネル。\r
40  */\r
41 @SuppressWarnings("serial")\r
42 public class AccountPanel\r
43         extends JDialog\r
44         implements ActionListener, ItemListener{\r
45 \r
46     private static final String FRAMETITLE =\r
47             "アカウント管理 - " + Jindolf.TITLE;\r
48 \r
49     private final Map<Land, String> landUserIDMap =\r
50             new HashMap<Land, String>();\r
51     private final Map<Land, char[]> landPasswordMap =\r
52             new HashMap<Land, char[]>();\r
53 \r
54     private final JComboBox landBox = new JComboBox();\r
55     private final JTextField idField = new JTextField(15);\r
56     private final JPasswordField pwField = new JPasswordField(15);\r
57     private final JButton loginButton = new JButton("ログイン");\r
58     private final JButton logoutButton = new JButton("ログアウト");\r
59     private final JButton closeButton = new JButton("閉じる");\r
60     private final JTextArea status = new JTextArea();\r
61 \r
62     /**\r
63      * アカウントパネルを生成。\r
64      * @param owner フレームオーナー\r
65      * @param landsModel 国モデル\r
66      * @throws java.lang.NullPointerException 引数がnull\r
67      */\r
68     public AccountPanel(Frame owner, LandsModel landsModel)\r
69             throws NullPointerException{\r
70         super(owner, FRAMETITLE, true);\r
71 \r
72         if(landsModel == null) throw new NullPointerException();\r
73         for(Land land : landsModel.getLandList()){\r
74             String userID = "";\r
75             char[] password = {};\r
76             this.landUserIDMap.put(land, userID);\r
77             this.landPasswordMap.put(land, password);\r
78             this.landBox.addItem(land);\r
79         }\r
80 \r
81         GUIUtils.modifyWindowAttributes(this, true, false, true);\r
82 \r
83         this.landBox.setToolTipText("アカウント管理する国を選ぶ");\r
84         this.idField.setToolTipText("IDを入力してください");\r
85         this.pwField.setToolTipText("パスワードを入力してください");\r
86 \r
87         Monodizer.monodize(this.idField);\r
88         Monodizer.monodize(this.pwField);\r
89 \r
90         this.idField.setMargin(new Insets(1, 4, 1, 4));\r
91         this.pwField.setMargin(new Insets(1, 4, 1, 4));\r
92 \r
93         this.idField.setComponentPopupMenu(new TextPopup());\r
94 \r
95         this.landBox.setEditable(false);\r
96         this.landBox.addItemListener(this);\r
97 \r
98         this.status.setEditable(false);\r
99         this.status.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));\r
100         this.status.setRows(2);\r
101         this.status.setLineWrap(true);\r
102 \r
103         this.loginButton.addActionListener(this);\r
104         this.logoutButton.addActionListener(this);\r
105         this.closeButton.addActionListener(this);\r
106 \r
107         getRootPane().setDefaultButton(this.loginButton);\r
108 \r
109         Container content = getContentPane();\r
110         GridBagLayout layout = new GridBagLayout();\r
111         GridBagConstraints constraints = new GridBagConstraints();\r
112         content.setLayout(layout);\r
113 \r
114         constraints.gridwidth = GridBagConstraints.REMAINDER;\r
115         constraints.weightx = 1.0;\r
116         constraints.insets = new Insets(5, 5, 5, 5);\r
117 \r
118         JComponent accountPanel = createCredential();\r
119         JComponent buttonPanel = createButtonPanel();\r
120 \r
121         constraints.weighty = 0.0;\r
122         constraints.fill = GridBagConstraints.HORIZONTAL;\r
123         content.add(accountPanel, constraints);\r
124 \r
125         Border border = BorderFactory.createTitledBorder("ログインステータス");\r
126         JPanel panel = new JPanel();\r
127         panel.setLayout(new BorderLayout());\r
128         panel.add(this.status, BorderLayout.CENTER);\r
129         panel.setBorder(border);\r
130 \r
131         constraints.weighty = 1.0;\r
132         constraints.fill = GridBagConstraints.BOTH;\r
133         content.add(panel, constraints);\r
134 \r
135         constraints.weighty = 0.0;\r
136         constraints.fill = GridBagConstraints.HORIZONTAL;\r
137         content.add(new JSeparator(), constraints);\r
138 \r
139         content.add(buttonPanel, constraints);\r
140 \r
141         preSelectActiveLand();\r
142 \r
143         updateGUI();\r
144 \r
145         return;\r
146     }\r
147 \r
148     /**\r
149      * 認証パネルを生成する。\r
150      * @return 認証パネル\r
151      */\r
152     private JComponent createCredential(){\r
153         JPanel credential = new JPanel();\r
154 \r
155         GridBagLayout layout = new GridBagLayout();\r
156         GridBagConstraints constraints = new GridBagConstraints();\r
157 \r
158         credential.setLayout(layout);\r
159 \r
160         constraints.insets = new Insets(5, 5, 5, 5);\r
161         constraints.fill = GridBagConstraints.NONE;\r
162 \r
163         constraints.anchor = GridBagConstraints.EAST;\r
164         credential.add(new JLabel("国名 :"), constraints);\r
165         constraints.anchor = GridBagConstraints.WEST;\r
166         credential.add(this.landBox, constraints);\r
167 \r
168         constraints.gridy = 1;\r
169         constraints.anchor = GridBagConstraints.EAST;\r
170         constraints.weightx = 0.0;\r
171         constraints.fill = GridBagConstraints.NONE;\r
172         credential.add(new JLabel("ID :"), constraints);\r
173         constraints.anchor = GridBagConstraints.WEST;\r
174         constraints.weightx = 1.0;\r
175         constraints.fill = GridBagConstraints.HORIZONTAL;\r
176         credential.add(this.idField, constraints);\r
177 \r
178         constraints.gridy = 2;\r
179         constraints.anchor = GridBagConstraints.EAST;\r
180         constraints.weightx = 0.0;\r
181         constraints.fill = GridBagConstraints.NONE;\r
182         credential.add(new JLabel("パスワード :"), constraints);\r
183         constraints.anchor = GridBagConstraints.WEST;\r
184         constraints.weightx = 1.0;\r
185         constraints.fill = GridBagConstraints.HORIZONTAL;\r
186         credential.add(this.pwField, constraints);\r
187 \r
188         return credential;\r
189     }\r
190 \r
191     /**\r
192      * ボタンパネルの作成。\r
193      * @return ボタンパネル\r
194      */\r
195     private JComponent createButtonPanel(){\r
196         JPanel buttonPanel = new JPanel();\r
197 \r
198         GridBagLayout layout = new GridBagLayout();\r
199         GridBagConstraints constraints = new GridBagConstraints();\r
200 \r
201         buttonPanel.setLayout(layout);\r
202 \r
203         constraints.fill = GridBagConstraints.NONE;\r
204         constraints.anchor = GridBagConstraints.WEST;\r
205         constraints.weightx = 0.0;\r
206         constraints.weighty = 0.0;\r
207 \r
208         buttonPanel.add(this.loginButton, constraints);\r
209 \r
210         constraints.insets = new Insets(0, 5, 0, 0);\r
211         buttonPanel.add(this.logoutButton, constraints);\r
212 \r
213         constraints.anchor = GridBagConstraints.EAST;\r
214         constraints.weightx = 1.0;\r
215         constraints.insets = new Insets(0, 15, 0, 0);\r
216         buttonPanel.add(this.closeButton, constraints);\r
217 \r
218         return buttonPanel;\r
219     }\r
220 \r
221     /**\r
222      * 現在コンボボックスで選択中の国を返す。\r
223      * @return 現在選択中のLand\r
224      */\r
225     private Land getSelectedLand(){\r
226         Land land = (Land)( this.landBox.getSelectedItem() );\r
227         return land;\r
228     }\r
229 \r
230     /**\r
231      * ACTIVEな最初の国がコンボボックスで既に選択されている状態にする。\r
232      */\r
233     private void preSelectActiveLand(){\r
234         for(int index = 0; index < this.landBox.getItemCount(); index++){\r
235             Object item = this.landBox.getItemAt(index);\r
236             Land land = (Land) item;\r
237             LandState state = land.getLandDef().getLandState();\r
238             if(state == LandState.ACTIVE){\r
239                 this.landBox.setSelectedItem(land);\r
240                 return;\r
241             }\r
242         }\r
243         return;\r
244     }\r
245 \r
246     /**\r
247      * 指定された国のユーザIDを返す。\r
248      * @param land 国\r
249      * @return ユーザID\r
250      */\r
251     private String getUserID(Land land){\r
252         return this.landUserIDMap.get(land);\r
253     }\r
254 \r
255     /**\r
256      * 指定された国のパスワードを返す。\r
257      * @param land 国\r
258      * @return パスワード\r
259      */\r
260     private char[] getPassword(Land land){\r
261         return this.landPasswordMap.get(land);\r
262     }\r
263 \r
264     /**\r
265      * ネットワークエラーを通知するモーダルダイアログを表示する。\r
266      * OKボタンを押すまでこのメソッドは戻ってこない。\r
267      * @param e ネットワークエラー\r
268      */\r
269     protected void showNetworkError(IOException e){\r
270         Jindolf.logger().warn(\r
271                 "アカウント処理中にネットワークのトラブルが発生しました", e);\r
272 \r
273         Land land = getSelectedLand();\r
274         ServerAccess server = land.getServerAccess();\r
275         String message =\r
276                 land.getLandDef().getLandName()\r
277                 +"を運営するサーバとの間の通信で"\r
278                 +"何らかのトラブルが発生しました。\n"\r
279                 +"相手サーバのURLは [ " + server.getBaseURL() + " ] だよ。\n"\r
280                 +"Webブラウザでも遊べないか確認してみてね!\n";\r
281 \r
282         JOptionPane pane = new JOptionPane(message,\r
283                                            JOptionPane.WARNING_MESSAGE,\r
284                                            JOptionPane.DEFAULT_OPTION );\r
285 \r
286         JDialog dialog = pane.createDialog(this,\r
287                                            "通信異常発生 - " + Jindolf.TITLE);\r
288 \r
289         dialog.pack();\r
290         dialog.setVisible(true);\r
291         dialog.dispose();\r
292 \r
293         return;\r
294     }\r
295 \r
296     /**\r
297      * アカウントエラーを通知するモーダルダイアログを表示する。\r
298      * OKボタンを押すまでこのメソッドは戻ってこない。\r
299      */\r
300     protected void showIllegalAccountDialog(){\r
301         Land land = getSelectedLand();\r
302         String message =\r
303                 land.getLandDef().getLandName()\r
304                 +"へのログインに失敗しました。\n"\r
305                 +"ユーザ名とパスワードは本当に正しいかな?\n"\r
306                 +"あなたは本当に [ " + getUserID(land) + " ] さんかな?\n"\r
307                 +"WebブラウザによるID登録手続きは本当に完了してるかな?\n"\r
308                 +"Webブラウザでもログインできないか試してみて!\n"\r
309                 +"…ユーザ名やパスワードにある種の特殊文字を使っている人は"\r
310                 +"問題があるかも。";\r
311 \r
312         JOptionPane pane = new JOptionPane(message,\r
313                                            JOptionPane.WARNING_MESSAGE,\r
314                                            JOptionPane.DEFAULT_OPTION );\r
315 \r
316         JDialog dialog =\r
317                 pane.createDialog(this, "ログイン認証失敗 - " + Jindolf.TITLE);\r
318 \r
319         dialog.pack();\r
320         dialog.setVisible(true);\r
321         dialog.dispose();\r
322 \r
323         return;\r
324     }\r
325 \r
326     /**\r
327      * 入力されたアカウント情報を基に現在選択中の国へログインする。\r
328      * @return ログインに成功すればtrueを返す。\r
329      */\r
330     protected boolean login(){\r
331         Land land = getSelectedLand();\r
332         ServerAccess server = land.getServerAccess();\r
333 \r
334         String id = this.idField.getText();\r
335         char[] password = this.pwField.getPassword();\r
336         this.landUserIDMap.put(land, id);\r
337         this.landPasswordMap.put(land, password);\r
338 \r
339         boolean result = false;\r
340         try{\r
341             result = server.login(id, password);\r
342         }catch(IOException e){\r
343             showNetworkError(e);\r
344             return false;\r
345         }\r
346 \r
347         if( ! result ){\r
348             showIllegalAccountDialog();\r
349         }\r
350 \r
351         return result;\r
352     }\r
353 \r
354     /**\r
355      * 現在選択中の国からログアウトする。\r
356      */\r
357     protected void logout(){\r
358         try{\r
359             logoutInternal();\r
360         }catch(IOException e){\r
361             showNetworkError(e);\r
362         }\r
363         return;\r
364     }\r
365 \r
366     /**\r
367      * 現在選択中の国からログアウトする。\r
368      * @throws java.io.IOException ネットワークエラー\r
369      */\r
370     protected void logoutInternal() throws IOException{\r
371         Land land = getSelectedLand();\r
372         ServerAccess server = land.getServerAccess();\r
373         server.logout();\r
374         return;\r
375     }\r
376 \r
377     /**\r
378      * 現在選択中の国のログイン状態に合わせてGUIを更新する。\r
379      */\r
380     private void updateGUI(){\r
381         Land land = getSelectedLand();\r
382         LandState state = land.getLandDef().getLandState();\r
383         ServerAccess server = land.getServerAccess();\r
384         boolean hasLoggedIn = server.hasLoggedIn();\r
385 \r
386         if(state != LandState.ACTIVE){\r
387             this.status.setText(\r
388                      "この国は既に募集を停止しました。\n"\r
389                     +"ログインは無意味です" );\r
390             this.idField.setEnabled(false);\r
391             this.pwField.setEnabled(false);\r
392             this.loginButton.setEnabled(false);\r
393             this.logoutButton.setEnabled(false);\r
394         }else if(hasLoggedIn){\r
395             this.status.setText("ユーザ [ " + getUserID(land) + " ] として\n"\r
396                           +"現在ログイン中です");\r
397             this.idField.setEnabled(false);\r
398             this.pwField.setEnabled(false);\r
399             this.loginButton.setEnabled(false);\r
400             this.logoutButton.setEnabled(true);\r
401         }else{\r
402             this.status.setText("現在ログインしていません");\r
403             this.idField.setEnabled(true);\r
404             this.pwField.setEnabled(true);\r
405             this.loginButton.setEnabled(true);\r
406             this.logoutButton.setEnabled(false);\r
407         }\r
408 \r
409         return;\r
410     }\r
411 \r
412     /**\r
413      * {@inheritDoc}\r
414      * ボタン操作のリスナ。\r
415      * @param event イベント {@inheritDoc}\r
416      */\r
417     // TODO Return キー押下によるログインもサポートしたい\r
418     @Override\r
419     public void actionPerformed(ActionEvent event){\r
420         Object source = event.getSource();\r
421 \r
422         if(source == this.closeButton){\r
423             setVisible(false);\r
424             dispose();\r
425             return;\r
426         }\r
427 \r
428         if(source == this.loginButton){\r
429             login();\r
430         }else if(source == this.logoutButton){\r
431             logout();\r
432         }\r
433 \r
434         updateGUI();\r
435 \r
436         return;\r
437     }\r
438 \r
439     /**\r
440      * {@inheritDoc}\r
441      * コンボボックス操作のリスナ。\r
442      * @param event イベント {@inheritDoc}\r
443      */\r
444     @Override\r
445     public void itemStateChanged(ItemEvent event){\r
446         Object source = event.getSource();\r
447         if(source != this.landBox) return;\r
448 \r
449         Land land = (Land) event.getItem();\r
450         String id;\r
451         char[] password;\r
452 \r
453         switch(event.getStateChange()){\r
454         case ItemEvent.SELECTED:\r
455             id = getUserID(land);\r
456             password = getPassword(land);\r
457             this.idField.setText(id);\r
458             this.pwField.setText(new String(password));\r
459             updateGUI();\r
460             break;\r
461         case ItemEvent.DESELECTED:\r
462             id = this.idField.getText();\r
463             password = this.pwField.getPassword();\r
464             this.landUserIDMap.put(land, id);\r
465             this.landPasswordMap.put(land, password);\r
466             break;\r
467         default:\r
468             assert false;\r
469             return;\r
470         }\r
471 \r
472         return;\r
473     }\r
474 \r
475     // TODO IDかパスワードが空の場合はログインボタンを無効にしたい\r
476 }\r