New research and technology page

* Added legacyworlds-server-beans-technologies Maven module, including
the player-level DAO and controller.

* Added session classes to carry technology information, modified web
client session façade accordingly

* Various changes to common UI elements (forms, lists, etc...) so the
start and end of some element can be drawn separately

* Added controller, templates and JavaScript for research page
This commit is contained in:
Emmanuel BENOîT 2012-04-07 13:06:03 +02:00
parent 154f215e24
commit 6dcd59d7bc
45 changed files with 2314 additions and 178 deletions
legacyworlds-server-beans-technologies/src
main
test
java
resources

View file

@ -0,0 +1,143 @@
package com.deepclone.lw.beans.game.technologies;
import java.sql.Types;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import com.deepclone.lw.cmd.player.gdata.empire.ResearchData;
import com.deepclone.lw.interfaces.game.technologies.PlayerTechnologiesDAO;
import com.deepclone.lw.utils.StoredProc;
/**
* Data access component for player technologies
*
* <p>
* This class implements database queries and calls to stored procedures which allow players to
* access and manage their empires' research.
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*/
class PlayerTechnologiesDAOBean
implements PlayerTechnologiesDAO
{
/** SQL query that fetches an empire's research state */
private static final String Q_EMPIRE_RESEARCH = "SELECT * FROM emp.technologies_v2_view WHERE empire_id = ?";
/** Row mapper for research state entries */
private final ResearchRowMapper mResearch;
/** Stored procedure that initiates a research priority update */
private StoredProc fResprioUpdateStart;
/** Stored procedure that uploads a single research priority */
private StoredProc fResprioUpdateSet;
/** Stored procedure that applies a research priority update */
private StoredProc fResprioUpdateApply;
/** Stored procedure that implements a technology */
private StoredProc fTechnologyImplement;
/** Spring JDBC interface */
private JdbcTemplate dTemplate;
/** Initialise the necessary row mappers */
public PlayerTechnologiesDAOBean( )
{
this.mResearch = new ResearchRowMapper( );
}
/**
* Dependency injector that sets the data source and initialises stored procedures
*
* @param dataSource
* the data source
*/
@Autowired( required = true )
public void setDataSource( DataSource dataSource )
{
this.dTemplate = new JdbcTemplate( dataSource );
this.fResprioUpdateStart = new StoredProc( dataSource , "emp" , "resprio_update_start" )
.addParameter( "_empire" , Types.INTEGER )
.addOutput( "_result" , Types.BOOLEAN );
this.fResprioUpdateSet = new StoredProc( dataSource , "emp" , "resprio_update_set" )
.addParameter( "_technology" , Types.VARCHAR )
.addParameter( "_priority" , Types.INTEGER )
.addOutput( "_result" , Types.BOOLEAN );
this.fResprioUpdateApply = new StoredProc( dataSource , "emp" , "resprio_update_apply" )
.addOutput( "_result" , Types.BOOLEAN );
this.fTechnologyImplement = new StoredProc( dataSource , "emp" , "technology_implement" )
.addParameter( "_empire" , Types.INTEGER )
.addParameter( "_technology" , Types.VARCHAR )
.addOutput( "_result" , Types.BOOLEAN );
}
/**
* Query and map research entries for an empire
*
* <p>
* Run the query against <code>emp.technologies_view</code> then map all rows using a
* {@link ResearchRowMapper}.
*/
@Override
public List< ResearchData > getResearchData( int empire )
{
return this.dTemplate.query( Q_EMPIRE_RESEARCH , this.mResearch , empire );
}
/**
* Call <code>emp.resprio_update_start</code>
*/
@Override
public boolean startPriorityUpdate( int empire )
{
return (Boolean) this.fResprioUpdateStart.execute( empire ).get( "_result" );
}
/**
* Call <code>emp.resprio_update_set</code>
*/
@Override
public boolean uploadResearchPriority( String identifier , int priority )
{
return (Boolean) this.fResprioUpdateSet.execute( identifier , priority ).get( "_result" );
}
/**
* Call <code>emp.resprio_update_apply</code>
*/
@Override
public boolean applyPriorityUpdate( )
{
return (Boolean) this.fResprioUpdateApply.execute( ).get( "_result" );
}
/**
* Call <code>emp.technology_implement</code>
*/
@Override
public boolean implement( int empire , String technology )
{
return (Boolean) this.fTechnologyImplement.execute( empire , technology ).get( "_result" );
}
}

View file

@ -0,0 +1,197 @@
package com.deepclone.lw.beans.game.technologies;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import com.deepclone.lw.cmd.player.gdata.GamePageData;
import com.deepclone.lw.cmd.player.gdata.empire.ResearchData;
import com.deepclone.lw.cmd.player.tech.GetResearchResponse;
import com.deepclone.lw.interfaces.game.EmpireManagement;
import com.deepclone.lw.interfaces.game.technologies.PlayerTechnologiesDAO;
import com.deepclone.lw.interfaces.game.technologies.ResearchController;
import com.deepclone.lw.interfaces.i18n.TranslationException;
import com.deepclone.lw.interfaces.i18n.Translator;
/**
* Research control component
*
* <p>
* This component implements all actions allowing a player to view and update its empire's research
* state.
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*/
@Transactional
class ResearchControllerBean
implements ResearchController
{
/** Main empire management component */
private EmpireManagement empireManagement;
/** String translator */
private Translator translator;
/** Data access object for technologies */
private PlayerTechnologiesDAO playerTechnologiesDAO;
/**
* Dependency injector that sets the empire management component
*
* @param empireManagement
* the empire management component
*/
@Autowired( required = true )
public void setEmpireManagement( EmpireManagement empireManagement )
{
this.empireManagement = empireManagement;
}
/**
* Dependency injector that sets the translation component
*
* @param translator
* the translation component
*/
@Autowired( required = true )
public void setTranslator( Translator translator )
{
this.translator = translator;
}
/**
* Dependency injector that sets the data access object for technologies
*
* @param playerTechnologiesDAO
* the data access object for technologies
*/
@Autowired( required = true )
public void setPlayerTechnologiesDAO( PlayerTechnologiesDAO playerTechnologiesDAO )
{
this.playerTechnologiesDAO = playerTechnologiesDAO;
}
/**
* Get the raw research data from the database, then handle translation and dependencies
*
* <p>
* In order to generate the response, this method fetches the raw research state information
* from the database. It then proceeds with translating all categories, names and descriptions,
* then adds reverse dependency information to all loaded technologies.
*/
@Override
public GetResearchResponse getResearchData( int empire )
{
GamePageData pageData = this.empireManagement.getGeneralInformation( empire );
List< ResearchData > technologies = this.playerTechnologiesDAO.getResearchData( empire );
// Translate categories, names and descriptions
Map< String , String > translations = this.getTranslationsFor( technologies , pageData.getLanguage( ) );
Map< String , ResearchData > byId = new HashMap< String , ResearchData >( );
for ( ResearchData tech : technologies ) {
byId.put( tech.getIdentifier( ) , tech );
tech.setCategory( translations.get( tech.getCategory( ) ) );
if ( tech.getName( ) != null ) {
tech.setName( translations.get( tech.getName( ) ) );
tech.setDescription( translations.get( tech.getDescription( ) ) );
}
}
// Add reverse dependency identifiers
for ( ResearchData tech : technologies ) {
for ( String dependency : tech.getDependencies( ) ) {
byId.get( dependency ).addReverseDependency( tech.getIdentifier( ) );
}
}
return new GetResearchResponse( pageData , technologies );
}
/**
* Get translations used by a list of raw research entries
*
* <p>
* This method is fed the raw research state data from the player technologies data access
* object. It generates a set of string identifiers which contains all categories, names and
* descriptions, then fetches these strings' translations.
*
* @param technologies
* the research state data from the DAO
* @param language
* the player's selected language
*
* @return the map of string identifiers to string contents
*/
private Map< String , String > getTranslationsFor( List< ResearchData > technologies , String language )
{
Set< String > identifiers = new HashSet< String >( );
for ( ResearchData tech : technologies ) {
identifiers.add( tech.getCategory( ) );
if ( tech.getName( ) != null ) {
identifiers.add( tech.getName( ) );
identifiers.add( tech.getDescription( ) );
}
}
try {
return this.translator.translate( language , identifiers );
} catch ( TranslationException e ) {
throw new RuntimeException( "error while translating technology-related data" , e );
}
}
/**
* Update research priorities
*
* <p>
* This method will start a research priority update for the specified empire, then upload each
* value in the map to the database. Finally, it will try to apply the update.
*
* <p>
* If any of the above steps fail, the method will abort.
*/
@Override
public boolean updatePriorities( int empire , Map< String , Integer > priorities )
{
if ( !this.playerTechnologiesDAO.startPriorityUpdate( empire ) ) {
return false;
}
for ( Map.Entry< String , Integer > entry : priorities.entrySet( ) ) {
if ( !this.playerTechnologiesDAO.uploadResearchPriority( entry.getKey( ) , entry.getValue( ) ) ) {
return false;
}
}
return this.playerTechnologiesDAO.applyPriorityUpdate( );
}
/**
* Implement a technology
*
* <p>
* Call the DAO's technology implementation method in order to access the corresponding stored
* procedure.
*/
@Override
public boolean implement( int empire , String technology )
{
return this.playerTechnologiesDAO.implement( empire , technology );
}
}

View file

@ -0,0 +1,69 @@
package com.deepclone.lw.beans.game.technologies;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.postgresql.util.PGobject;
import org.springframework.jdbc.core.RowMapper;
import com.deepclone.lw.cmd.player.gdata.empire.ResearchData;
/**
* Row mapper for empire research state entries
*
* <p>
* This class is responsible for converting empire research state information rows into instances of
* the corresponding class, {@link ResearchData}. The instances it produces are "raw": they contain
* string identifiers instead of translations, and their reverse dependencies are not set.
*
* @author <a href="mailto:tseeker@legacyworlds.com">E. Benoît</a>
*
*/
class ResearchRowMapper
implements RowMapper< ResearchData >
{
/**
* Map a row from <code>emp.technologies_view</code>
*
* <p>
* This method maps a single row from the <code>emp.technologies_view</code> view into a
* {@link ResearchData} instance.
*/
@Override
public ResearchData mapRow( ResultSet rs , int rowNum )
throws SQLException
{
ResearchData output = new ResearchData( );
output.setIdentifier( rs.getString( "emptech_id" ) );
output.setCategory( rs.getString( "technology_category" ) );
if ( rs.getBoolean( "emptech_visible" ) ) {
output.setName( rs.getString( "technology_name" ) );
output.setDescription( rs.getString( "technology_description" ) );
output.setPrice( rs.getLong( "technology_price" ) );
}
int ratio = rs.getInt( "emptech_ratio" );
if ( rs.wasNull( ) ) {
String state = ( (PGobject) rs.getObject( "emptech_state" ) ).getValue( );
output.setImplemented( "KNOWN".equals( state ) );
} else {
output.setCompletion( ratio );
output.setPriority( rs.getInt( "emptech_priority" ) );
}
String dependencies = rs.getString( "technology_dependencies" );
if ( ! "".equals( dependencies ) ) {
output.setDependencies( dependencies.split( "," ) );
}
return output;
}
}

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
<bean id="playerTechnologiesDAO" class="com.deepclone.lw.beans.game.technologies.PlayerTechnologiesDAOBean" />
<bean id="reesearchController" class="com.deepclone.lw.beans.game.technologies.ResearchControllerBean" />
</beans>