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 }