diff --git a/build/post-build.d/20-import-tools.sh b/build/post-build.d/20-import-tools.sh index 2b20f04..de69c46 100644 --- a/build/post-build.d/20-import-tools.sh +++ b/build/post-build.d/20-import-tools.sh @@ -33,6 +33,7 @@ EOF java legacyworlds-server-main-*.jar --run-tool ImportText data/i18n-text.xml || exit 1 java legacyworlds-server-main-*.jar --run-tool ImportResources data/resources.xml || exit 1 java legacyworlds-server-main-*.jar --run-tool ImportTechs data/techs.xml || exit 1 +java legacyworlds-server-main-*.jar --run-tool ImportTechGraph data/tech-graph.xml || exit 1 java legacyworlds-server-main-*.jar --run-tool ImportBuildables data/buildables.xml || exit 1 java legacyworlds-server-main-*.jar & diff --git a/legacyworlds-server-data/db-structure/parts/040-functions/030-tech.sql b/legacyworlds-server-data/db-structure/parts/040-functions/030-tech.sql index 5b2e90b..2a1f4be 100644 --- a/legacyworlds-server-data/db-structure/parts/040-functions/030-tech.sql +++ b/legacyworlds-server-data/db-structure/parts/040-functions/030-tech.sql @@ -324,6 +324,47 @@ GRANT EXECUTE +/* + * Remove all dependencies for a technology + * ----------------------------------------- + * + * This stored procedure removes all dependencies and reverse dependencies for + * some technology. + * + * Parameters: + * _technology The name of the technology + */ +DROP FUNCTION IF EXISTS defs.techdep_clear( TEXT ); +CREATE FUNCTION defs.techdep_clear( _technology TEXT ) + RETURNS VOID + LANGUAGE SQL + STRICT VOLATILE + SECURITY DEFINER +AS $techdep_clear$ + + DELETE FROM defs.technology_dependencies + WHERE technology_name_id = ( + SELECT id FROM defs.strings + WHERE name = $1 + ); + + DELETE FROM defs.technology_dependencies + WHERE technology_name_id_depends = ( + SELECT id FROM defs.strings + WHERE name = $1 + ); + +$techdep_clear$; + +REVOKE EXECUTE + ON FUNCTION defs.techdep_clear( TEXT ) + FROM PUBLIC; +GRANT EXECUTE + ON FUNCTION defs.techdep_clear( TEXT ) + TO :dbuser; + + + -- ******************************************************** -- OLD CODE BELOW -- ******************************************************** diff --git a/legacyworlds-server-data/db-structure/tests/admin/040-functions/030-tech/060-techdep-clear.sql b/legacyworlds-server-data/db-structure/tests/admin/040-functions/030-tech/060-techdep-clear.sql new file mode 100644 index 0000000..ef2dacc --- /dev/null +++ b/legacyworlds-server-data/db-structure/tests/admin/040-functions/030-tech/060-techdep-clear.sql @@ -0,0 +1,58 @@ +/* + * Test the defs.techdep_clear() function + */ +BEGIN; + \i utils/strings.sql + -- Make the columns we don't use in the technology definition table NULL-able + ALTER TABLE defs.technologies + ALTER technology_category_id DROP NOT NULL , + ALTER technology_discovery_id DROP NOT NULL , + ALTER technology_description_id DROP NOT NULL , + ALTER technology_price DROP NOT NULL , + ALTER technology_points DROP NOT NULL; + + -- Create strings to use as the technologies' names + SELECT _create_test_strings( 4 , 'tech' ); + + -- Insert the technologies + INSERT INTO defs.technologies ( technology_name_id ) + VALUES ( _get_string( 'tech1' ) ) , + ( _get_string( 'tech2' ) ) , + ( _get_string( 'tech3' ) ) , + ( _get_string( 'tech4' ) ); + + -- Add a chain of dependencies + INSERT INTO defs.technology_dependencies( + technology_name_id , technology_name_id_depends + ) VALUES ( _get_string( 'tech2' ) , _get_string( 'tech1' ) ) , + ( _get_string( 'tech3' ) , _get_string( 'tech2' ) ) , + ( _get_string( 'tech4' ) , _get_string( 'tech3' ) ); + + + -- ***** TESTS BEGIN HERE ***** + SELECT plan( 4 ); + + SELECT diag_test_name( 'defs.techdep_clear() - No failure on invalid technology name' ); + SELECT lives_ok( $$ + SELECT defs.techdep_clear( 'does not exist' ) + $$ ); + + SELECT diag_test_name( 'defs.techdep_clear() - Successful call' ); + SELECT lives_ok( $$ + SELECT defs.techdep_clear( 'tech2' ) + $$ ); + + SELECT diag_test_name( 'defs.techdep_clear() - Cleared technology has no dependencies' ); + SELECT is_empty( $$ + SELECT * FROM defs.technology_dependencies + WHERE technology_name_id = _get_string( 'tech2' ); + $$ ); + + SELECT diag_test_name( 'defs.techdep_clear() - Cleared technology has no reverse dependencies' ); + SELECT is_empty( $$ + SELECT * FROM defs.technology_dependencies + WHERE technology_name_id_depends = _get_string( 'tech2' ); + $$ ); + + SELECT * FROM finish( ); +ROLLBACK; \ No newline at end of file diff --git a/legacyworlds-server-data/db-structure/tests/user/040-functions/030-tech/040-techdep-clear.sql b/legacyworlds-server-data/db-structure/tests/user/040-functions/030-tech/040-techdep-clear.sql new file mode 100644 index 0000000..fb9f70b --- /dev/null +++ b/legacyworlds-server-data/db-structure/tests/user/040-functions/030-tech/040-techdep-clear.sql @@ -0,0 +1,13 @@ +/* + * Test privileges on defs.techdep_remove() + */ +BEGIN; + SELECT plan( 1 ); + + SELECT diag_test_name( 'defs.techdep_clear() - EXECUTE privilege' ); + SELECT lives_ok( $$ + SELECT defs.techdep_clear( '' ); + $$ ); + + SELECT * FROM finish( ); +ROLLBACK; \ No newline at end of file diff --git a/legacyworlds-server-main/data/tech-graph.xml b/legacyworlds-server-main/data/tech-graph.xml new file mode 100644 index 0000000..4f54016 --- /dev/null +++ b/legacyworlds-server-main/data/tech-graph.xml @@ -0,0 +1,42 @@ + + + + + + + + + cruisersTech + + + + bCruisersTech + + + + + + + + indFactTech + + + + reanimationTech + + + \ No newline at end of file diff --git a/legacyworlds-server-main/data/tech-graph.xsd b/legacyworlds-server-main/data/tech-graph.xsd new file mode 100644 index 0000000..4c3b555 --- /dev/null +++ b/legacyworlds-server-main/data/tech-graph.xsd @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/ImportTechGraph.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/ImportTechGraph.java new file mode 100644 index 0000000..c912c05 --- /dev/null +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/ImportTechGraph.java @@ -0,0 +1,326 @@ +package com.deepclone.lw.cli; + + +import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.sql.DataSource; + +import org.apache.log4j.Logger; +import org.postgresql.util.PGobject; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.AbstractApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.context.support.FileSystemXmlApplicationContext; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; + +import com.deepclone.lw.cli.xmlimport.TechnologyLoader; +import com.deepclone.lw.cli.xmlimport.data.DataImportException; +import com.deepclone.lw.cli.xmlimport.data.techs.Technologies; +import com.deepclone.lw.cli.xmlimport.data.techs.Technology; +import com.deepclone.lw.cli.xmlimport.data.techs.TechnologyDefinitionResult; +import com.deepclone.lw.utils.StoredProc; + + + +/** + * Technology graph import tool + * + *

+ * This class defines the body of the technology graph import tool. It loads the data file specified + * on the command line, validates it and then loads the technology definitions and dependencies into + * the database. + * + * @author E. Benoît + */ +public class ImportTechGraph + extends CLITool +{ + /** Logging system */ + private final Logger logger = Logger.getLogger( ImportTechGraph.class ); + + /** File to read the definitions from */ + private File file; + + /** Spring transaction template */ + private TransactionTemplate tTemplate; + + /** Technology creation or update stored procedure */ + private StoredProc fUocTechnology; + + /** Stored procedure that removes all dependencies from a technology */ + private StoredProc fTechdepClear; + + /** Stored procedure that adds a dependency to a technology */ + private StoredProc fTechdepAdd; + + + /** + * Create the Spring context + * + *

+ * Load the definition of the data source as a Spring context, then adds the transaction + * management component. + * + * @return the Spring application context + */ + private ClassPathXmlApplicationContext createContext( ) + { + FileSystemXmlApplicationContext ctx = new FileSystemXmlApplicationContext( new String[] { + this.getDataSource( ) + } ); + ctx.refresh( ); + + return new ClassPathXmlApplicationContext( new String[] { + "configuration/transactions.xml" + } , true , ctx ); + } + + + /** + * Create database access templates + * + *

+ * Initialise the transaction template and the stored procedure wrappers. + * + * @param ctx + * the Spring application context + */ + private void createTemplates( ApplicationContext ctx ) + { + DataSource dSource = ctx.getBean( DataSource.class ); + PlatformTransactionManager tManager = ctx.getBean( PlatformTransactionManager.class ); + + this.fUocTechnology = new StoredProc( dSource , "defs" , "uoc_technology" ) + .addParameter( "_name" , java.sql.Types.VARCHAR ).addParameter( "_category" , java.sql.Types.VARCHAR ) + .addParameter( "_discovery" , java.sql.Types.VARCHAR ) + .addParameter( "_description" , java.sql.Types.VARCHAR ) + .addParameter( "_price" , java.sql.Types.BIGINT ).addParameter( "_points" , java.sql.Types.BIGINT ) + .addOutput( "_result" , java.sql.Types.OTHER ); + + this.fTechdepClear = new StoredProc( dSource , "defs" , "techdep_clear" ).addParameter( "_tech" , + java.sql.Types.VARCHAR ); + + this.fTechdepAdd = new StoredProc( dSource , "defs" , "techdep_add" ) + .addParameter( "_dependent" , java.sql.Types.VARCHAR ) + .addParameter( "_dependency" , java.sql.Types.VARCHAR ).addOutput( "_result" , java.sql.Types.OTHER ); + + this.tTemplate = new TransactionTemplate( tManager ); + } + + + /** + * Import all technology definitions + * + *

+ * Import all technology definitions from the top-level {@link Technologies}, creating a map of + * dependencies. Once all definitions have been imported, add all dependencies. + * + * @param data + * the top level {@link Technologies} data instance + * + * @throws DataImportException + * when some technology definition or dependency cannot be imported + */ + private void importTechnologies( Technologies data ) + throws DataImportException + { + Map< String , List< String > > dependencies = new HashMap< String , List< String > >( ); + for ( Technology technology : data ) { + this.importTechnologyDefinition( technology ); + dependencies.put( technology.getName( ) , technology.getDependencies( ) ); + } + + for ( Map.Entry< String , List< String > > techDeps : dependencies.entrySet( ) ) { + for ( String dependency : techDeps.getValue( ) ) { + this.importDependency( techDeps.getKey( ) , dependency ); + } + } + } + + + /** + * Import a technology definition + * + *

+ * Import the definition, and if there was already a definition by that name, clear its + * dependencies. + * + * @param technology + * the definition to import + * @throws DataImportException + * when the update or creation stored procedure returns anything other than + * CREATED or UPDATED + */ + private void importTechnologyDefinition( Technology technology ) + throws DataImportException + { + this.logger.info( "Importing technology '" + technology.getName( ) + "'" ); + + TechnologyDefinitionResult result = this.getResultOf( this.fUocTechnology.execute( technology.getName( ) , + technology.getCategory( ) , technology.getDiscovery( ) , technology.getDescription( ) , + technology.getCost( ) , technology.getPoints( ) ) ); + if ( result == TechnologyDefinitionResult.UPDATED ) { + this.fTechdepClear.execute( technology.getName( ) ); + } else if ( result != TechnologyDefinitionResult.CREATED ) { + throw new DataImportException( "Error while importing technology '" + technology.getName( ) + "': " + + result ); + } + } + + + /** + * Import a dependency between two technologies + * + *

+ * Attempt to add a dependency from a technology to another. + * + * @param dependent + * the dependent technology + * @param dependency + * the technology to depend on + * + * @throws DataImportException + * if dependency creation fails + */ + private void importDependency( String dependent , String dependency ) + throws DataImportException + { + this.logger.info( "Importing dependency from " + dependent + " to " + dependency ); + + TechnologyDefinitionResult result = this.getResultOf( this.fTechdepAdd.execute( dependent , dependency ) ); + switch ( result ) { + case CREATED: + return; + case REDUNDANT: + throw new DataImportException( "( " + dependent + " -> " + dependency + " ) is redundant" ); + case BAD_STRINGS: + throw new DataImportException( "( " + dependent + " -> " + dependency + " ): undefined technology" ); + case CYCLE: + throw new DataImportException( "( " + dependent + " -> " + dependency + " ) would create a cycle" ); + default: + throw new DataImportException( "( " + dependent + " -> " + dependency + " ): unexpected error code " + + result ); + } + } + + + /** + * Helper method for stored procedure results + * + *

+ * This method converts the map returned by one of the definition stored procedures into a + * {@link TechnologyDefinitionResult} value. + * + * @param spResult + * the return value of {@link StoredProc#execute(Object...)} + * + * @return the converted value of the _result field + */ + private TechnologyDefinitionResult getResultOf( Map< String , Object > spResult ) + { + return TechnologyDefinitionResult.valueOf( ( (PGobject) spResult.get( "_result" ) ).getValue( ) ); + } + + + /** + * Run the technology definitions import tool + * + *

+ * Loads the data file, the connects to the database and creates or updates all definitions. + */ + @Override + public void run( ) + { + Technologies data; + try { + data = new TechnologyLoader( this.file ).load( ); + } catch ( DataImportException e ) { + this.logger.error( "Error while loading technology definitions" , e ); + return; + } + + AbstractApplicationContext ctx = this.createContext( ); + this.createTemplates( ctx ); + this.executeImportTransaction( data ); + ToolBase.destroyContext( ctx ); + } + + + /** + * Execute the technology definitions importation transaction + * + *

+ * Run a transaction and execute the importation code inside it. Roll back if anything goes + * wrong. + * + * @param data + * the {@link Technologies} definitions instance + */ + private void executeImportTransaction( final Technologies data ) + { + boolean rv = this.tTemplate.execute( new TransactionCallback< Boolean >( ) { + @Override + public Boolean doInTransaction( TransactionStatus status ) + { + boolean rv = ImportTechGraph.this.doTransaction( data ); + if ( !rv ) { + status.setRollbackOnly( ); + } + return rv; + } + } ); + if ( rv ) { + this.logger.info( "Technology import successful" ); + } + } + + + /** + * Import transaction body + * + *

+ * Import all definitions and handle exceptions. + * + * @param data + * the {@link Technologies} definitions instance + * @return + */ + private boolean doTransaction( Technologies data ) + { + try { + this.importTechnologies( data ); + return true; + } catch ( RuntimeException e ) { + this.logger.error( "Caught runtime exception" , e ); + } catch ( DataImportException e ) { + this.logger.error( e.getMessage( ) ); + } + return false; + } + + + /** + * Obtain the name of the definitions file + * + *

+ * Check the command line options, setting the definitions file accordingly. + */ + @Override + public boolean setOptions( String... options ) + { + if ( options.length != 1 ) { + return false; + } + this.file = new File( options[ 0 ] ); + if ( ! ( this.file.isFile( ) && this.file.canRead( ) ) ) { + return false; + } + return true; + } +} diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/TechnologyLoader.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/TechnologyLoader.java new file mode 100644 index 0000000..275a7cf --- /dev/null +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/TechnologyLoader.java @@ -0,0 +1,116 @@ +package com.deepclone.lw.cli.xmlimport; + + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; +import com.deepclone.lw.cli.xmlimport.data.techs.Technologies; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.XStreamException; + + + +/** + * Technology Loader + * + *

+ * This class can be used to load all technology definitions. It extracts them from the XML file and + * verifies them. + * + * @author E. Benoît + */ +public class TechnologyLoader +{ + /** The file to read the XML from */ + private final File file; + + + /** + * Initialise the loader + * + * @param file + * the XML file that contains the definitions + */ + public TechnologyLoader( File file ) + { + this.file = file.getAbsoluteFile( ); + } + + + /** + * Initialise the necessary XStream instance + * + *

+ * Initialise the XStream instance by processing the annotations of all the technology + * importable data classes. + * + * @return the XStream instance to use when loading the data + */ + private XStream initXStream( ) + { + XStream xstream = new XStream( ); + xstream.processAnnotations( Technologies.class ); + return xstream; + } + + + /** + * Load the technology definitions + * + *

+ * Load the XML file and process the definitions using XStream. + * + * @return the top-level importable data instance + * + * @throws DataImportException + * if reading from the file or parsing its contents fail + */ + private Technologies loadXMLFile( ) + throws DataImportException + { + FileInputStream fis; + try { + fis = new FileInputStream( this.file ); + } catch ( FileNotFoundException e ) { + throw new DataImportException( "Unable to load technology definitions" , e ); + } + + try { + try { + XStream xstream = this.initXStream( ); + return (Technologies) xstream.fromXML( fis ); + } finally { + fis.close( ); + } + } catch ( IOException e ) { + throw new DataImportException( "Input error while loading technology definitions" , e ); + } catch ( XStreamException e ) { + throw new DataImportException( "XML error while loading technology definitions" , e ); + } + } + + + /** + * Load and process technology definitions + * + *

+ * Attempt to load all technology definitions, ensure they are valid, then set the original + * file's path. + * + * @return the top-level importable data instance + * + * @throws DataImportException + * if loading or verifying the data fails + */ + public Technologies load( ) + throws DataImportException + { + Technologies techs = this.loadXMLFile( ); + techs.verifyData( ); + techs.setReadFrom( this.file ); + return techs; + } +} diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/resources/BasicResource.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/resources/BasicResource.java index 551ff47..86a8442 100644 --- a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/resources/BasicResource.java +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/resources/BasicResource.java @@ -64,9 +64,9 @@ public class BasicResource throw new DataImportException( "Missing name string" ); } if ( this.description == null || "".equals( this.description.trim( ) ) ) { - throw new DataImportException( "Missing name string" ); + throw new DataImportException( "Missing description string" ); } - if ( this.category != null && "".equals( this.description.trim( ) ) ) { + if ( this.category != null && "".equals( this.category.trim( ) ) ) { throw new DataImportException( "Category invalid" ); } if ( this.weight == null ) { diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technologies.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technologies.java new file mode 100644 index 0000000..842f64c --- /dev/null +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technologies.java @@ -0,0 +1,98 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; +import com.deepclone.lw.cli.xmlimport.data.ImportableData; +import com.thoughtworks.xstream.annotations.XStreamAlias; +import com.thoughtworks.xstream.annotations.XStreamImplicit; + + + +/** + * Technology definitions data + * + *

+ * This class represents the contents of a technology definition XML file. It contains a list of + * technology definitions. + * + * @author E. Benoît + */ +@SuppressWarnings( "serial" ) +@XStreamAlias( "lw-tech-graph" ) +public class Technologies + extends ImportableData + implements Iterable< Technology > +{ + /** All present technology definitions */ + @XStreamImplicit( itemFieldName = "technology" ) + private final List< Technology > technologies = new LinkedList< Technology >( ); + + + /** + * Check the technology data + * + *

+ * Check whether there is a list of technologies, then check each definition to make sure that + * it is valid and that its name is unique. + */ + @Override + public void verifyData( ) + throws DataImportException + { + if ( this.technologies == null ) { + throw new DataImportException( "No technology definitions" ); + } + + HashSet< String > names = new HashSet< String >( ); + for ( Technology tech : this.technologies ) { + tech.verifyData( ); + this.checkUniqueItem( names , tech.getName( ) ); + } + } + + + /** + * Checks that the name of a technology is unique + * + *

+ * This helper method is used by {@link #verifyData()} to make sure technology names are unique. + * + * @param existing + * the existing set of names + * @param value + * the name to check + * + * @throws DataImportException + * if the name is already present in the set of existing names + */ + public void checkUniqueItem( HashSet< String > existing , String value ) + throws DataImportException + { + if ( existing.contains( value ) ) { + throw new DataImportException( "Duplicate technology name '" + value + "'" ); + } + existing.add( value ); + } + + + /** + * Technology definition iterator + * + *

+ * Grant access to the list of technologies in read-only mode. + * + * @return a read-only iterator on the list of technologies. + */ + @Override + public Iterator< Technology > iterator( ) + { + return Collections.unmodifiableList( this.technologies ).iterator( ); + } + +} diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technology.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technology.java new file mode 100644 index 0000000..49b6368 --- /dev/null +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/Technology.java @@ -0,0 +1,183 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; +import com.deepclone.lw.cli.xmlimport.data.ImportableData; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; +import com.thoughtworks.xstream.annotations.XStreamImplicit; + + + +/** + * A technology definition + * + *

+ * This class represents a technology definition that can be imported from the data file. It + * contains all required technology attributes, as well as a list of strings for the dependencies. + * + * @author E. Benoît + */ +@SuppressWarnings( "serial" ) +public class Technology + extends ImportableData +{ + + /** Identifier of the name string */ + @XStreamAsAttribute + private String name; + + /** Identifier of the category string */ + @XStreamAsAttribute + private String category; + + /** Identifier of the description string */ + @XStreamAsAttribute + private String description; + + /** Identifier of the string to display on technology discovery */ + @XStreamAsAttribute + private String discovery; + + /** Monetary cost of the technology */ + @XStreamAsAttribute + private Long cost; + + /** Required amount of research points */ + @XStreamAsAttribute + private Long points; + + /** List of technology name string identifiers describing the dependencies */ + @XStreamImplicit( itemFieldName = "depends-on" ) + private final List< String > dependencies = new LinkedList< String >( ); + + + /** + * Technology definition verifier + * + *

+ * Make sure that all technology definition fields are both present and valid. If dependencies + * exist, also verify the names' validity. + */ + @Override + public void verifyData( ) + throws DataImportException + { + if ( this.name == null || "".equals( this.name.trim( ) ) ) { + throw new DataImportException( "Missing name string" ); + } + if ( this.category == null || "".equals( this.category.trim( ) ) ) { + throw new DataImportException( "Missing category string in " + this.name ); + } + if ( this.description == null || "".equals( this.description.trim( ) ) ) { + throw new DataImportException( "Missing description string in " + this.name ); + } + if ( this.discovery == null || "".equals( this.discovery.trim( ) ) ) { + throw new DataImportException( "Missing discovery string in " + this.name ); + } + + if ( this.points == null ) { + throw new DataImportException( "Missing research points in " + this.name ); + } else if ( this.points <= 0 ) { + throw new DataImportException( "Invalid research points in " + this.name ); + } + + if ( this.cost == null ) { + throw new DataImportException( "Missing cost in " + this.name ); + } else if ( this.cost <= 0 ) { + throw new DataImportException( "Invalid cost in " + this.name ); + } + + if ( this.dependencies != null ) { + for ( String dep : this.dependencies ) { + if ( dep == null || "".equals( dep.trim( ) ) ) { + throw new DataImportException( "Empty dependency name in " + this.name ); + } + } + } + } + + + /** + * Gets the identifier of the name string. + * + * @return the identifier of the name string + */ + public String getName( ) + { + return this.name; + } + + + /** + * Gets the identifier of the category string. + * + * @return the identifier of the category string + */ + public String getCategory( ) + { + return this.category; + } + + + /** + * Gets the identifier of the description string. + * + * @return the identifier of the description string + */ + public String getDescription( ) + { + return this.description; + } + + + /** + * Gets the identifier of the string to display on technology discovery. + * + * @return the identifier of the string to display on technology discovery + */ + public String getDiscovery( ) + { + return this.discovery; + } + + + /** + * Gets the monetary cost of the technology. + * + * @return the monetary cost of the technology + */ + public Long getCost( ) + { + return this.cost; + } + + + /** + * Gets the required amount of research points. + * + * @return the required amount of research points + */ + public Long getPoints( ) + { + return this.points; + } + + + /** + * Gets the list of technology name string identifiers describing the dependencies. + * + * @return the list of technology name string identifiers describing the dependencies + */ + public List< String > getDependencies( ) + { + if ( this.dependencies == null ) { + return Collections.emptyList( ); + } + return Collections.unmodifiableList( this.dependencies ); + } + +} diff --git a/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/TechnologyDefinitionResult.java b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/TechnologyDefinitionResult.java new file mode 100644 index 0000000..e7491bb --- /dev/null +++ b/legacyworlds-server-main/src/main/java/com/deepclone/lw/cli/xmlimport/data/techs/TechnologyDefinitionResult.java @@ -0,0 +1,47 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +/** + * Return codes for technology definition manipulation stored procedures + * + *

+ * This enumeration represents the various values which can be returned by the stored procedures + * which manipulate technology definitions and technology dependencies. + * + * @author E. Benoît + */ +public enum TechnologyDefinitionResult { + + /** The technology definition or dependency was created */ + CREATED , + + /** The technology definition was updated */ + UPDATED , + + /** The dependency was deleted */ + DELETED , + + /** The specified dependency does not exist */ + MISSING , + + /** One (or more) of the numeric parameters is invalid */ + BAD_VALUE , + + /** + * The name, description, discovery or category string identifiers were not valid string + * identifiers. + */ + BAD_STRINGS , + + /** + * The specified description and/or discovery string was in use by another technology. + */ + DUP_STRING , + + /** The dependency would cause a cycle */ + CYCLE , + + /** The dependency would be redundant */ + REDUNDANT + +} diff --git a/legacyworlds-server-tests/TestFiles/technology-loader/bad-contents.xml b/legacyworlds-server-tests/TestFiles/technology-loader/bad-contents.xml new file mode 100644 index 0000000..272dba0 --- /dev/null +++ b/legacyworlds-server-tests/TestFiles/technology-loader/bad-contents.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/legacyworlds-server-tests/TestFiles/technology-loader/bad-data.xml b/legacyworlds-server-tests/TestFiles/technology-loader/bad-data.xml new file mode 100644 index 0000000..584e660 --- /dev/null +++ b/legacyworlds-server-tests/TestFiles/technology-loader/bad-data.xml @@ -0,0 +1,8 @@ + + + + + + \ No newline at end of file diff --git a/legacyworlds-server-tests/TestFiles/technology-loader/bad-xml.xml b/legacyworlds-server-tests/TestFiles/technology-loader/bad-xml.xml new file mode 100644 index 0000000..ee1d0ed --- /dev/null +++ b/legacyworlds-server-tests/TestFiles/technology-loader/bad-xml.xml @@ -0,0 +1,2 @@ +This is not an XML file, obviously. +We'll make that even more confusing: <<<<<< & >>!!! \ No newline at end of file diff --git a/legacyworlds-server-tests/TestFiles/technology-loader/good-data.xml b/legacyworlds-server-tests/TestFiles/technology-loader/good-data.xml new file mode 100644 index 0000000..0cf7e8e --- /dev/null +++ b/legacyworlds-server-tests/TestFiles/technology-loader/good-data.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestI18NLoader.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestI18NLoader.java index 0b2ac67..a44feed 100644 --- a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestI18NLoader.java +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestI18NLoader.java @@ -1,7 +1,11 @@ package com.deepclone.lw.cli.xmlimport; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.io.File; import java.io.IOException; @@ -11,7 +15,6 @@ import org.junit.Test; import com.deepclone.lw.cli.xmlimport.data.DataImportException; import com.deepclone.lw.cli.xmlimport.data.i18n.I18NText; import com.deepclone.lw.cli.xmlimport.data.i18n.LanguageDefinition; -import com.deepclone.lw.cli.xmlimport.data.i18n.StringDefinition; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.io.StreamException; @@ -129,22 +132,8 @@ public class TestI18NLoader assertNotNull( text ); int lCount = 0; - for ( LanguageDefinition ld : text ) { - assertEquals( "test" , ld.getId( ) ); - assertEquals( "test" , ld.getName( ) ); - - int tCount = 0; - for ( StringDefinition sd : ld ) { - assertEquals( "test" , sd.getId( ) ); - try { - assertEquals( "test" , sd.getString( ).trim( ) ); - } catch ( DataImportException e ) { - fail( "could not load string: " + e.getMessage( ) ); - } - tCount++; - } - - assertEquals( 1 , tCount ); + for ( @SuppressWarnings( "unused" ) + LanguageDefinition ld : text ) { lCount++; } assertEquals( 1 , lCount ); diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestResourceLoader.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestResourceLoader.java index d49210d..def4308 100644 --- a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestResourceLoader.java +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestResourceLoader.java @@ -14,7 +14,6 @@ import org.junit.Test; import com.deepclone.lw.cli.xmlimport.data.DataImportException; import com.deepclone.lw.cli.xmlimport.data.resources.BasicResource; -import com.deepclone.lw.cli.xmlimport.data.resources.NaturalResource; import com.deepclone.lw.cli.xmlimport.data.resources.Resources; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.io.StreamException; @@ -127,27 +126,8 @@ public class TestResourceLoader // Not sure if this is the best way to code for the two different resource types... int rCount = 0; - for ( BasicResource br : resources ) { - if ( rCount == 0 ) { - assertEquals( "money" , br.getName( ) ); - assertEquals( "moneyDescription" , br.getDescription( ) ); - assertEquals( new Integer( 0 ) , br.getWeight( ) ); - assertEquals( null , br.getCategory( ) ); - } else if ( rCount == 1 ) { - // This isn't retarded is it? - NaturalResource nr = (NaturalResource) br; - assertEquals( "titanium" , nr.getName( ) ); - assertEquals( "titaniumDescription" , nr.getDescription( ) ); - assertEquals( new Integer( 1 ) , nr.getWeight( ) ); - assertEquals( "minerals" , nr.getCategory( ) ); - assertEquals( new Double( 0.8 ) , nr.getPresenceProbability( ) ); - assertEquals( new Double( 5000 ) , nr.getQuantity( ).getAverage( ) ); - assertEquals( new Double( 1500 ) , nr.getQuantity( ).getDeviation( ) ); - assertEquals( new Double( 0.1 ) , nr.getDifficulty( ).getAverage( ) ); - assertEquals( new Double( 0.05 ) , nr.getDifficulty( ).getDeviation( ) ); - assertEquals( new Double( 0.4 ) , nr.getRecovery( ).getAverage( ) ); - assertEquals( new Double( 0.05 ) , nr.getRecovery( ).getDeviation( ) ); - } + for ( @SuppressWarnings( "unused" ) + BasicResource br : resources ) { rCount++; } assertEquals( 2 , rCount ); diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestTechnologyLoader.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestTechnologyLoader.java new file mode 100644 index 0000000..ee24fc4 --- /dev/null +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/TestTechnologyLoader.java @@ -0,0 +1,132 @@ +package com.deepclone.lw.cli.xmlimport; + + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.File; +import java.io.IOException; + +import org.junit.Test; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; +import com.deepclone.lw.cli.xmlimport.data.techs.Technologies; +import com.deepclone.lw.cli.xmlimport.data.techs.Technology; +import com.thoughtworks.xstream.converters.ConversionException; +import com.thoughtworks.xstream.io.StreamException; + + + +/** + * Unit tests for the {@link TechnologyLoader} class. + * + * @author E. Benoît + */ +public class TestTechnologyLoader +{ + /** + * Try initialising the loader with a null file instance. + * + * @throws NullPointerException + * when the constructor is executed + */ + @Test( expected = NullPointerException.class ) + public void testNullFile( ) + throws NullPointerException + { + new TechnologyLoader( null ); + } + + + /** Try loading a file that does not exist */ + @Test + public void testMissingFile( ) + { + TechnologyLoader loader = new TechnologyLoader( new File( "does-not-exist" ) ); + try { + loader.load( ); + } catch ( DataImportException e ) { + assertTrue( "cause is a" + e.getCause( ).getClass( ).getName( ) , e.getCause( ) instanceof IOException ); + return; + } + fail( "no exception after trying to load a missing file" ); + } + + + /** Try loading a file that contains something that is not XML. */ + @Test + public void testBadXML( ) + { + TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-xml.xml" ) ); + try { + loader.load( ); + } catch ( DataImportException e ) { + assertTrue( "cause is a " + e.getCause( ).getClass( ).getName( ) , e.getCause( ) instanceof StreamException ); + return; + } + fail( "no exception after loading stuff that isn't XML" ); + } + + + /** + * Test loading a file that contains XML but which cannot be deserialised to a + * {@link Technologies} instance. + */ + @Test + public void testBadContents( ) + { + TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-contents.xml" ) ); + try { + loader.load( ); + } catch ( DataImportException e ) { + assertTrue( "cause is a " + e.getCause( ).getClass( ).getName( ) , + e.getCause( ) instanceof ConversionException ); + return; + } + fail( "no exception after loading bad XML" ); + } + + + /** + * Try loading a file that contains valid XML for a {@link Technologies} instance with semantic + * errors. + */ + @Test + public void testBadData( ) + { + TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/bad-data.xml" ) ); + try { + loader.load( ); + } catch ( DataImportException e ) { + assertNull( e.getCause( ) ); + return; + } + fail( "no exception after loading bad data" ); + } + + + /** Try loading valid data, make sure that it contains one record */ + @Test + public void testGoodData( ) + { + TechnologyLoader loader = new TechnologyLoader( new File( "TestFiles/technology-loader/good-data.xml" ) ); + Technologies technologies; + try { + technologies = loader.load( ); + } catch ( DataImportException e ) { + fail( "could not load valid file" ); + return; + } + assertNotNull( technologies ); + + int rCount = 0; + for ( @SuppressWarnings( "unused" ) + Technology tech : technologies ) { + rCount++; + } + assertEquals( 1 , rCount ); + } +} diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/resources/BaseTest.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/resources/BaseTest.java index d578d51..9f2eb11 100644 --- a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/resources/BaseTest.java +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/resources/BaseTest.java @@ -22,7 +22,7 @@ import com.thoughtworks.xstream.XStreamException; * */ -abstract public class BaseTest +abstract class BaseTest { /** * Escape &, < and > in XML strings diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/BaseTest.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/BaseTest.java new file mode 100644 index 0000000..2d70331 --- /dev/null +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/BaseTest.java @@ -0,0 +1,175 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +import com.deepclone.lw.cli.xmlimport.data.ImportableData; +import com.thoughtworks.xstream.XStream; +import com.thoughtworks.xstream.XStreamException; + + + +/** + * Base class for testing technology importation + * + *

+ * This class is used as a parent for tests of the technology import structures. It includes the + * code used to actually create these technology definitions, as this can normally only be done + * through XStream. + * + * @author E. Benoît + * + */ + +abstract class BaseTest +{ + /** + * Escape &, < and > in XML strings + * + * @param string + * the string to escape + * + * @return the escaped string + */ + private String xmlString( String string ) + { + return string.replace( "&" , "&" ).replace( "<" , "<" ).replace( ">" , ">" ); + } + + + /** + * Escape &, < and >, ' and " in XML strings + * + * @param string + * the string to escape + * + * @return the escaped string + */ + private String quoteString( String string ) + { + return "\"" + this.xmlString( string ).replace( "\"" , """ ).replace( "'" , "'" ) + "\""; + } + + + /** + * Create the XML code for a technology definition + * + *

+ * This method generates the XML code for a technology definition, which can then be imported + * using XStream into a {@link Technology} instance. It is capable of generating instances with + * various fields set to null. + * + * @param name + * identifier of the technology's name string + * @param category + * identifier of the technology's category string + * @param discovery + * identifier of the technology's discovery string + * @param description + * identifier of the technology's description string + * @param cost + * monetary cost of implementing the technology + * @param points + * amount of research points required + * @param dependencies + * dependencies as an array of strings + * @return + */ + protected String createTechnology( String name , String category , String discovery , String description , + Long cost , Long points , String... dependencies ) + { + StringBuilder builder = new StringBuilder( "" ); + } else { + builder.append( ">" ); + for ( String dep : dependencies ) { + builder.append( "" ); + if ( dep != null ) { + builder.append( this.xmlString( dep ) ); + } + builder.append( "" ); + } + builder.append( "" ); + } + + return builder.toString( ); + } + + + /** + * Creates the XML code for a top-level technology definition element + * + * @param resources + * XML definitions of technologies + * + * @return the top-level element's XML code + */ + protected String createTopLevel( String... technologies ) + { + StringBuilder str = new StringBuilder( ); + str.append( "" ); + for ( String technology : technologies ) { + str.append( technology ); + } + str.append( "" ); + return str.toString( ); + } + + + /** + * Create the necessary XStream instance + * + *

+ * Initialise an XStream instance and set it up so it can process technology data definitions. + * This includes annotation processing, of course, but also generating an alias allowing the + * <technology> tag to be recognised on its own. + */ + private XStream createXStreamInstance( ) + { + XStream xstream = new XStream( ); + xstream.processAnnotations( Technologies.class ); + xstream.alias( "technology" , Technology.class ); + return xstream; + } + + + /** + * Create a resource object from its XML code + * + * @param xml + * the XML code + * @param cls + * the class of the object + * + * @return the object that was created from the code + * + * @throws ClassCastException + * if the code corresponds to some other type of object + * @throws XStreamException + * if some error occurred while deserialising the object + */ + protected < T extends ImportableData > T createObject( String xml , Class< T > cls ) + throws ClassCastException , XStreamException + { + return cls.cast( this.createXStreamInstance( ).fromXML( xml ) ); + } +} diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnologies.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnologies.java new file mode 100644 index 0000000..bd5c6ac --- /dev/null +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnologies.java @@ -0,0 +1,96 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; + + + +/** + * Unit tests for the {@link Technologies} class + * + * @author E. Benoît + */ +public class TestTechnologies + extends BaseTest +{ + + /** + * Test loading an empty list of technologies + * + *

+ * {@link Technologies#iterator()} will throw {@link NullPointerException} when there are no + * definitions. Use that to determine if it is empty. + */ + @Test( expected = NullPointerException.class ) + public void testEmpty( ) + { + String defs = this.createTopLevel( ); + Technologies techs = this.createObject( defs , Technologies.class ); + techs.iterator( ); + } + + + /** + * Test loading a list of technologies + */ + @Test + public void testTechs( ) + { + String tDef = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ); + String defs = this.createTopLevel( tDef ); + Technologies techs = this.createObject( defs , Technologies.class ); + + int i = 0; + for ( Technology technology : techs ) { + assertNotNull( technology ); + assertEquals( 0 , i ); + i++; + } + } + + + /** + * Test verifying an empty list of technologies + */ + @Test( expected = DataImportException.class ) + public void testVerifyEmpty( ) + throws DataImportException + { + String defs = this.createTopLevel( ); + Technologies techs = this.createObject( defs , Technologies.class ); + techs.verifyData( ); + } + + + /** + * Test verifying a list of technologies with an invalid technology in it + */ + @Test( expected = DataImportException.class ) + public void testVerifyInvalid( ) + throws DataImportException + { + String tDef = this.createTechnology( null , "category" , "discovery" , "description" , 12L , 34L ); + String defs = this.createTopLevel( tDef ); + Technologies techs = this.createObject( defs , Technologies.class ); + techs.verifyData( ); + } + + + /** + * Test verifying a valid list of technologies + */ + @Test + public void testVerifyOk( ) + throws DataImportException + { + String tDef = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ); + String defs = this.createTopLevel( tDef ); + Technologies techs = this.createObject( defs , Technologies.class ); + techs.verifyData( ); + } + +} diff --git a/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnology.java b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnology.java new file mode 100644 index 0000000..4b76abc --- /dev/null +++ b/legacyworlds-server-tests/src/test/java/com/deepclone/lw/cli/xmlimport/data/techs/TestTechnology.java @@ -0,0 +1,329 @@ +package com.deepclone.lw.cli.xmlimport.data.techs; + + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.deepclone.lw.cli.xmlimport.data.DataImportException; + + + +/** + * Test the {@link Technology} class + * + * @author E. Benoît + */ +public class TestTechnology + extends BaseTest +{ + + /** + * Test loading a definition with no dependencies + */ + @Test + public void testLoadNoDeps( ) + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + + assertNotNull( technology ); + assertEquals( "name" , technology.getName( ) ); + assertEquals( "category" , technology.getCategory( ) ); + assertEquals( "discovery" , technology.getDiscovery( ) ); + assertEquals( "description" , technology.getDescription( ) ); + assertEquals( new Long( 12 ) , technology.getCost( ) ); + assertEquals( new Long( 34 ) , technology.getPoints( ) ); + + assertNotNull( technology.getDependencies( ) ); + assertTrue( technology.getDependencies( ).isEmpty( ) ); + } + + + /** + * Test loading a definition with dependencies + */ + @Test + public void testLoadDeps( ) + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L , + "dep1" , "dep2" ); + Technology technology = this.createObject( definition , Technology.class ); + + assertNotNull( technology ); + assertEquals( "name" , technology.getName( ) ); + assertEquals( "category" , technology.getCategory( ) ); + assertEquals( "discovery" , technology.getDiscovery( ) ); + assertEquals( "description" , technology.getDescription( ) ); + assertEquals( new Long( 12 ) , technology.getCost( ) ); + assertEquals( new Long( 34 ) , technology.getPoints( ) ); + + assertNotNull( technology.getDependencies( ) ); + assertEquals( 2 , technology.getDependencies( ).size( ) ); + assertEquals( "dep1" , technology.getDependencies( ).get( 0 ) ); + assertEquals( "dep2" , technology.getDependencies( ).get( 1 ) ); + } + + + /** + * Test {@link Technology#verifyData()} when the name is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyNameNull( ) + throws DataImportException + { + String definition = this.createTechnology( null , "category" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the name is empty + */ + @Test( expected = DataImportException.class ) + public void testVerifyNameEmpty( ) + throws DataImportException + { + String definition = this.createTechnology( "" , "category" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the name consists of spaces only + */ + @Test( expected = DataImportException.class ) + public void testVerifyNameSpaces( ) + throws DataImportException + { + String definition = this.createTechnology( " " , "category" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the category is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyCategoryNull( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , null , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the category is empty + */ + @Test( expected = DataImportException.class ) + public void testVerifyCategoryEmpty( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the category consists of spaces only + */ + @Test( expected = DataImportException.class ) + public void testVerifyCategorySpaces( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , " " , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the discovery string is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyDiscoveryNull( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , null , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the discovery string is empty + */ + @Test( expected = DataImportException.class ) + public void testVerifyDiscoveryEmpty( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the discovery string consists of spaces only + */ + @Test( expected = DataImportException.class ) + public void testVerifyDiscoverySpaces( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , " " , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the description is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyDescriptionNull( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , null , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the description is empty + */ + @Test( expected = DataImportException.class ) + public void testVerifyDescriptionEmpty( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the description consists of spaces only + */ + @Test( expected = DataImportException.class ) + public void testVerifyDescriptionSpaces( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , " " , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the cost is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyCostNull( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , null , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the cost is 0 + */ + @Test( expected = DataImportException.class ) + public void testVerifyCostZero( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 0L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the amount of points is null + */ + @Test( expected = DataImportException.class ) + public void testVerifyPointsNull( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , null ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when the amount of points is 0 + */ + @Test( expected = DataImportException.class ) + public void testVerifyPointsZero( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 0L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} on a valid definition with no dependencies + */ + @Test + public void testVerifyValidNoDeps( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when a dependency is empty + */ + @Test( expected = DataImportException.class ) + public void testVerifyDepsEmpty( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L , "" ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} when a dependency consists of spaces only + */ + @Test( expected = DataImportException.class ) + public void testVerifyDepsSpaces( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L , + " " ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + + + /** + * Test {@link Technology#verifyData()} on a valid definition with dependencies + */ + @Test + public void testVerifyValidDeps( ) + throws DataImportException + { + String definition = this.createTechnology( "name" , "category" , "discovery" , "description" , 12L , 34L , + "dep1" , "dep2" ); + Technology technology = this.createObject( definition , Technology.class ); + technology.verifyData( ); + } + +} diff --git a/legacyworlds/doc/local-deployment.txt b/legacyworlds/doc/local-deployment.txt index 24669c3..b31c0ff 100644 --- a/legacyworlds/doc/local-deployment.txt +++ b/legacyworlds/doc/local-deployment.txt @@ -41,6 +41,8 @@ from the root of the server's distribution: --run-tool ImportText data/i18n-text.xml java -jar legacyworlds-server-main-1.0.0-0.jar \ --run-tool ImportTechs data/techs.xml + java -jar legacyworlds-server-main-1.0.0-0.jar \ + --run-tool ImportTechGraph data/tech-graph.xml java -jar legacyworlds-server-main-1.0.0-0.jar \ --run-tool ImportResources data/resources.xml java -jar legacyworlds-server-main-1.0.0-0.jar \