View Javadoc

1   /*
2    * All Rights Reserved.
3    * Copyright (C) 1999-2005 Tsukuba Bunko.
4    *
5    * Licensed under the BSD License ("the License"); you may not use
6    * this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *       http://www.tsukuba-bunko.org/licenses/LICENSE.txt
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   *
17   * $Id: SessionManager.java,v 1.3 2005/09/04 16:10:14 ppoi Exp $
18   */
19  package tsukuba_bunko.peko.session;
20  
21  import java.io.File;
22  import java.io.FileInputStream;
23  import java.io.FileOutputStream;
24  import java.io.ObjectInputStream;
25  import java.io.ObjectOutputStream;
26  import java.net.JarURLConnection;
27  import java.net.URL;
28  import java.net.URLConnection;
29  
30  import java.text.DecimalFormat;
31  
32  import java.util.Date;
33  import java.util.HashSet;
34  
35  import javax.swing.JOptionPane;
36  
37  import tsukuba_bunko.peko.Logger;
38  import tsukuba_bunko.peko.PekoSystem;
39  
40  import tsukuba_bunko.peko.resource.ResourceManager;
41  import tsukuba_bunko.peko.session.MessageIDs;
42  
43  
44  /***
45   * セーブデータの管理を行います。
46   * @author	$Author: ppoi $
47   * @version	$Revision: 1.3 $
48   */
49  public class SessionManager	{
50  
51  	/***
52  	 * セーブデータ ID のフォーマッタ
53  	 */
54  	protected static final DecimalFormat	FORMAT = new DecimalFormat( "000" );
55  
56  
57  	/***
58  	 * 前回選択したインデックス
59  	 */
60  	protected int	_lastIndex = -1;
61  
62  	/***
63  	 * システムセーブデータ
64  	 */
65  	protected SystemSaveData	_systemSaveData = null;
66  
67  	/***
68  	 * 現在のセッション
69  	 */
70  	protected Session	_session = null;
71  
72  
73  	/***
74  	 * <code>SessionManager</code> のインスタンスを生成します。
75  	 */
76  	public SessionManager()
77  	{
78  		super();
79  
80  		try	{
81  			_systemSaveData = loadSystemSaveData();
82  		}
83  		catch( Exception e )	{
84  			Logger.debug( "[session.manager] fail to load SystemSaveData.", e );
85  		}
86  
87  		_session = new Session();
88  		_session.setSessionFlagSet( new HashSet(89) );
89  		if( _systemSaveData != null )	{
90  			_session.setSystemFlagSet( _systemSaveData.getSystemFlagSet(), _systemSaveData.getSaveDataInfo().getTimestamp() );
91  		}
92  		else	{
93  			_systemSaveData = new SystemSaveData();
94  			SaveDataInfo	info = new SaveDataInfo();
95  			info.setID( -1 );
96  			info.setTitle( "PVNS System Save Data" );
97  			_systemSaveData.setSaveDataInfo( info );
98  
99  			_session.setSystemFlagSet( new HashSet(89), null );
100 		}
101 	}
102 
103 
104 	/***
105 	 * 現在のセッションを初期化します。
106 	 */
107 	public void initializeSession()
108 	{
109 		_session.setSessionFlagSet( new HashSet(89) );
110 		_session.setSceneContext( null );
111 	}
112 
113 	/***
114 	 * 現在のセッションを取得します。
115 	 * @return	現在のセッション
116 	 */
117 	public Session getSession()
118 	{
119 		return _session;
120 	}
121 
122 	/***
123 	 * システムセーブデータを取得します。
124 	 * @return	システムセーブデータ
125 	 */
126 	public SystemSaveData getSystemSaveData()
127 	{
128 		return _systemSaveData;
129 	}
130 
131 
132 	/***
133 	 * 現在のセッションを保存します。
134 	 */
135 	public void saveCurrentSession()
136 		throws SaveFailureException
137 	{
138 		SaveData	saveData = new SaveData();
139 		SaveDataInfo	saveDataInfo = new SaveDataInfo();
140 		saveDataInfo.setTitle( _session.getSceneContext().getSceneTitle() );
141 
142 		saveData.setSaveDataInfo( saveDataInfo );
143 		saveData.setSession( _session );
144 
145 		save( saveData );
146 	}
147 
148 
149 	/***
150 	 * セーブデータ一覧を取得します。
151 	 * @param	beginIndex	開始インデックス
152 	 * @param	size	取得サイズ
153 	 * @return	セーブデータ一覧
154 	 */
155 	protected SaveDataInfo[] getSaveDataInfoList( int beginIndex, int size )
156 	{
157 		SaveDataInfo[]	list = new SaveDataInfo[ size ];
158 		File	saveFile = null;
159 		SaveData	saveData = null;
160 		SaveDataInfo	saveDataInfo = null;
161 		Object	data = null;
162 		FileInputStream	fis = null;
163 		ObjectInputStream	ois = null;
164 		for( int i = 0; i < list.length; ++i )	{
165 			saveFile = getSaveFile( i + beginIndex );
166 			if( saveFile.isFile() )	{
167 				try	{
168 					fis = new FileInputStream( saveFile );
169 					ois = new ObjectInputStream( fis );
170 					data = ois.readObject();
171 				}
172 				catch( Exception e )	{
173 					Logger.warn( MessageIDs.SAV0008W, new Object[]{String.valueOf(i + beginIndex)}, e );
174 				}
175 				finally	{
176 					if( ois != null )	{
177 						try	{
178 							ois.close();
179 						}
180 						catch( Exception e )	{
181 							Logger.warn( MessageIDs.SAV0004W, e );
182 						}
183 					}
184 					else if( fis != null )	{
185 						try	{
186 							fis.close();
187 						}
188 						catch( Exception e )	{
189 							Logger.warn( MessageIDs.SAV0004W, e );
190 						}
191 					}
192 					ois = null;
193 					fis = null;
194 				}
195 
196 				if( data instanceof SaveData )	{
197 					saveData = (SaveData)data;
198 					if( saveData != null )	{
199 						saveDataInfo = saveData.getSaveDataInfo();
200 						if( (saveDataInfo != null) && (saveDataInfo.getTitle() != null) && (saveDataInfo.getTimestamp() != null) )	{
201 							list[i] = saveDataInfo;
202 						}
203 					}
204 					else	{
205 						Logger.warn( MessageIDs.SAV0022W, new Object[]{String.valueOf(i)} );
206 					}
207 				}
208 			}
209 		}
210 
211 		return list;
212 	}
213 
214 	/***
215 	 * セーブデータを保存します。
216 	 * @param	data	保存するセーブデータ
217 	 * @throws	SaveFailureException
218 	 */
219 	protected void save( SaveData data )
220 		throws SaveFailureException
221 	{
222 		SaveDataInfo[]	list = getSaveDataInfoList( 0, 20 );
223 		int	index = SaveDataDialog.showDialog( list, getInitialSelectedIndexForSave(list), true );
224 		Logger.debug( "index: " + index );
225 		if( index == -1 )	{
226 			return;
227 		}
228 		else	{
229 			data.getSaveDataInfo().setID( index );
230 		}
231 		Date	timestamp = new Date();
232 		data.getSaveDataInfo().setTimestamp( timestamp );
233 
234 		FileOutputStream	fos = null;
235 		ObjectOutputStream	oos = null;
236 		try	{
237 			File	path = getSaveFile( data.getSaveDataInfo().getID() );
238 			Logger.debug( "save to " + path.getAbsolutePath() );
239 
240 			fos = new FileOutputStream( path );
241 
242 			oos = new ObjectOutputStream( fos );
243 			oos.writeObject( data );
244 			oos.flush();
245 			_lastIndex = index;
246 		}
247 		catch( Exception e )	{
248 			Logger.error( MessageIDs.SAV0001E, new Object[]{String.valueOf(data.getSaveDataInfo().getID())}, e );
249 			SaveFailureException	sfe = new SaveFailureException( "fail to save.", e );
250 			throw sfe;
251 		}
252 		finally	{
253 			if( oos != null )	{
254 				try	{
255 					oos.close();
256 				}
257 				catch( Exception e )	{
258 					Logger.warn( MessageIDs.SAV0002W, e );
259 				}
260 			}
261 			else if( fos != null )	{
262 				try	{
263 					fos.close();
264 				}
265 				catch( Exception e )	{
266 					Logger.warn( MessageIDs.SAV0002W, e );
267 				}
268 			}
269 		}
270 	}
271 
272 
273 
274 	/***
275 	 * セーブデータを読み込みます。
276 	 * @return	セーブデータをロードした場合 <code>true</code>,ロードしなかった場合 <code>false</code>。
277 	 * @throws	LoadFailureException	ロードに失敗した場合
278 	 */
279 	public boolean load()
280 		throws LoadFailureException
281 	{
282 		SaveDataInfo[]	list = getSaveDataInfoList( 0, 20 );
283 		boolean	nodata = false;
284 		if( list != null )	{
285 			nodata = true;
286 			for( int i = 0; i < list.length; ++i )	{
287 				if( list[i] != null )	{
288 					nodata = false;
289 				}
290 			}
291 		}
292 		else	{
293 			nodata = true;
294 		}
295 
296 		if( nodata )	{
297 			ResourceManager	resources = ResourceManager.getInstance();
298 			String	title = (String)resources.getResource( ResourceIDs.DIALOG_NOTIFY_NODATA_TITLE );
299 			if( title == null )	{
300 				Logger.warn( MessageIDs.SAV0020W );
301 			}
302 			String	message = (String)resources.getResource( ResourceIDs.DIALOG_NOTIFY_NODATA_MESSAGE );
303 			if( message == null )	{
304 				message = "No save data for loading.";
305 				Logger.warn( MessageIDs.SAV0021W, new Object[]{"\"" + message + "\""} );
306 			}
307 			JOptionPane.showMessageDialog( PekoSystem.getInstance().getMainWindow(), message, title, JOptionPane.INFORMATION_MESSAGE );
308 			return false;
309 		}
310 
311 		int	index = SaveDataDialog.showDialog( list, getInitialSelectedIndexForLoad(list), false );
312 		if( index == -1 )	{
313 			return false;
314 		}
315 
316 		File	path = getSaveFile( index );
317 		if( !path.exists() )	{
318 			Logger.error( MessageIDs.SAV0005E, new Object[]{String.valueOf(index)} );
319 			LoadFailureException	lfe = new LoadFailureException( "no such save file" );
320 			throw lfe;
321 		}
322 
323 		FileInputStream	fis = null;
324 		ObjectInputStream	ois = null;
325 		try	{
326 			fis = new FileInputStream( path );
327 			ois = new ObjectInputStream( fis );
328 
329 			SaveData	data = (SaveData)ois.readObject();
330 			if( !isValidData(data) )	{
331 				Logger.error( MessageIDs.SAV0025E );
332 				return false;
333 			}
334 
335 			Session	session = data.getSession();
336 			session.setSystemFlagSet( _session.getSystemFlagSet(), _session.getTimestamp() );
337 			_session = session;
338 
339 			_lastIndex = index;
340 			return true;
341 		}
342 		catch( Exception e )	{
343 			Logger.error( MessageIDs.SAV0003E, new Object[]{String.valueOf(String.valueOf(index))}, e );
344 			LoadFailureException	lfe = new LoadFailureException( "fail to load.", e );
345 			throw lfe;
346 		}
347 		finally	{
348 			if( ois != null )	{
349 				try	{
350 					ois.close();
351 				}
352 				catch( Exception e )	{
353 					Logger.warn( MessageIDs.SAV0004W, e );
354 				}
355 			}
356 			else if( fis != null )	{
357 				try	{
358 					fis.close();
359 				}
360 				catch( Exception e )	{
361 					Logger.warn( MessageIDs.SAV0004W, e );
362 				}
363 			}
364 		}
365 	}
366 
367 	/***
368 	 * システムセーブデータを保存します。
369 	 */
370 	public void saveSystemSaveData()
371 		throws SaveFailureException
372 	{
373 		Logger.debug( "save SystemSaveData." );
374 		SystemSaveData	data = _systemSaveData;
375 		data.setSystemFlagSet( _session.getSystemFlagSet() );
376 		FileOutputStream	fos = null;
377 		ObjectOutputStream	oos = null;
378 		try	{
379 			data.getSaveDataInfo().setTimestamp( new Date() );
380 
381 			File	path = getSystemSaveFile();
382 
383 			fos = new FileOutputStream( path );
384 
385 			oos = new ObjectOutputStream( fos );
386 			oos.writeObject( data );
387 			oos.flush();
388 		}
389 		catch( Exception e )	{
390 			Logger.error( MessageIDs.SAV0006F, e );
391 			SaveFailureException	sfe = new SaveFailureException( "fail to save system save data.", e );
392 			throw sfe;
393 		}
394 		finally	{
395 			if( oos != null )	{
396 				try	{
397 					oos.close();
398 				}
399 				catch( Exception e )	{
400 					Logger.warn( MessageIDs.SAV0002W, e );
401 				}
402 			}
403 			else if( fos != null )	{
404 				try	{
405 					fos.close();
406 				}
407 				catch( Exception e )	{
408 					Logger.warn( MessageIDs.SAV0002W, e );
409 				}
410 			}
411 		}
412 	}
413 
414 	/***
415 	 * システムセーブデータを読み込みます。
416 	 * @return	システムセーブデータ
417 	 * @throws	LoadFailureException	読み込みに失敗した場合
418 	 */
419 	public SystemSaveData loadSystemSaveData()
420 		throws LoadFailureException
421 	{
422 		SystemSaveData	saveData = null;
423 
424 		File	path = getSystemSaveFile();
425 		if( !path.exists() )	{
426 			return null;
427 		}
428 
429 		FileInputStream	fis = null;
430 		ObjectInputStream	ois = null;
431 		try	{
432 			fis = new FileInputStream( path );
433 			ois = new ObjectInputStream( fis );
434 
435 			saveData = (SystemSaveData)ois.readObject();
436 		}
437 		catch( Exception e )	{
438 			Logger.fatal( MessageIDs.SAV0006F, e );
439 			LoadFailureException	lfe = new LoadFailureException( "fail to load.", e );
440 			throw lfe;
441 		}
442 		finally	{
443 			if( ois != null )	{
444 				try	{
445 					ois.close();
446 				}
447 				catch( Exception e )	{
448 					Logger.warn( MessageIDs.SAV0004W, e );
449 				}
450 			}
451 			else if( fis != null )	{
452 				try	{
453 					fis.close();
454 				}
455 				catch( Exception e )	{
456 					Logger.warn( MessageIDs.SAV0004W, e );
457 				}
458 			}
459 		}
460 
461 		return saveData;
462 	}
463 
464 	/***
465 	 * <code>id</code> で識別されるセーブデータを格納するセーブファイルを取得します。
466 	 * @param	id	セーブデータ ID
467 	 * @return	セーブファイル
468 	 */
469 	protected File getSaveFile( int id )
470 	{
471 		ResourceManager	resources = ResourceManager.getInstance();
472 		return new File( resources.getLocationResources().getSaveDirectory(), "save" + FORMAT.format(new Integer(id)) + ".dat" );
473 	}
474 
475 	/***
476 	 * システムの状態を保存するセーブデータを格納するセーブファイルを取得します。
477 	 * @return	セーブファイル
478 	 */
479 	protected File getSystemSaveFile()
480 	{
481 		ResourceManager	resources = ResourceManager.getInstance();
482 		return new File( resources.getLocationResources().getSaveDirectory(), "config.dat" );
483 	}
484 
485 	/***
486 	 * セーブ時に最初に選択されているインデックスを取得します。
487 	 * @param	list	一覧
488 	 * @return	セーブ時に最初に選択されているインデックス
489 	 */
490 	protected int getInitialSelectedIndexForSave( SaveDataInfo[] list )
491 	{
492 		if( (list == null) || (list.length == 0) )	{
493 			throw new IllegalArgumentException( "void list is specified." );
494 		}
495 
496 		int	selected = 0;
497 		SaveDataInfo	info = null;
498 		for( int i = 0; i < list.length; ++i )	{
499 			info = list[i];
500 			if( info == null )	{
501 				selected = i;
502 				break;
503 			}
504 			else	{
505 				if( (list[selected] == null) || info.getTimestamp().before(list[selected].getTimestamp()) )	{
506 					selected = i;
507 				}
508 			}
509 		}
510 		return selected;
511 	}
512 
513 	/***
514 	 * ロード時に最初に選択されているインデックスを取得します。
515 	 * @param	list	一覧
516 	 * @return	ロード時に最初に選択されているインデックス
517 	 */
518 	protected int getInitialSelectedIndexForLoad( SaveDataInfo[] list )
519 	{
520 		if( (list == null) || (list.length == 0) )	{
521 			throw new IllegalArgumentException( "void list is specified." );
522 		}
523 
524 		if( _lastIndex != -1 )	{
525 			return _lastIndex;
526 		}
527 
528 		int	selected = 0;
529 		SaveDataInfo	info = null;
530 		for( int i = 0; i < list.length; ++i )	{
531 			info = list[i];
532 			if( info != null )	{
533 				if( (list[selected] == null) || info.getTimestamp().after(list[selected].getTimestamp()) )	{
534 					selected = i;
535 				}
536 			}
537 		}
538 		return selected;
539 	}
540 
541 	/***
542 	 * セーブデータの妥当性検証をします。
543 	 * @param	data	セーブデータ
544 	 * @return	妥当な場合 <code>true</code>,使用不能な場合 <code>false</code>
545 	 */
546 	protected boolean isValidData( SaveData data )
547 	{
548 		String	sceneName = data.getSession().getSceneContext().getSceneName();
549 		URL	sceneURL = getSceneURL( sceneName );
550 		long	lastModified = 0L;
551 		try	{
552 			URLConnection	connection = sceneURL.openConnection();
553 			connection.connect();
554 			if( connection instanceof JarURLConnection )	{
555 				JarURLConnection	jarconnection = (JarURLConnection)connection;
556 				lastModified = jarconnection.getJarEntry().getTime();
557 			}
558 			else	{
559 				lastModified = connection.getLastModified();
560 			}
561 			Logger.info( "last modified time=" + new java.util.Date(lastModified) + ", scene=" + sceneName );
562 		}
563 		catch( Exception ex )	{
564 			Logger.warn( "fail to get last modified timestamp.", ex );
565 		}
566 
567 		if( lastModified > 0L )	{
568 			return (data.getSaveDataInfo().getTimestamp().getTime() >= lastModified);
569 		}
570 		else	{
571 			return true;
572 		}
573 	}
574 
575 	/***
576 	 * シーンデータの URL を取得します。
577 	 * @param	scene	シーン名
578 	 * @return	シーンデータの URL
579 	 */
580 	protected URL getSceneURL( String scene )
581 	{
582 		ResourceManager	resources = ResourceManager.getInstance();
583 		URL	sceneDir = resources.getLocationResources().getScenesDirecotryURL();
584 		try	{
585 			return new URL( sceneDir, scene );
586 		}
587 		catch( Exception e )	{
588 			Logger.fatal( tsukuba_bunko.peko.scenario.MessageIDs.SCN0006F, e );
589 			PekoSystem.showErrorDialog( tsukuba_bunko.peko.scenario.MessageIDs.SCN0006F.getMessage(), e, true );
590 			return null;
591 		}
592 	}
593 }