Resource Definition Loader

* Implemented resource definition loader including tests

* Added resource definition xml file and style definition

* Made a small style change to i18n loader
This commit is contained in:
Tim Rosser 2012-01-10 22:38:29 +11:00
parent 3637b6e1d1
commit f4a16aa431
20 changed files with 2275 additions and 3 deletions

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<lw-resources xmlns="http://www.deepclone.com/lw/b6/m2/resources"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.deepclone.com/lw/b6/m2/resources
resources.xsd">
<basic-resource name="money" description="moneyDescription"
weight="0" /> <!-- This could have a category="" as well -->
<natural-resource name="titanium" description="titaniumDescription"
category="minerals" weight="1" presence-probability="0.8">
<quantity average="5000" deviation="1500" />
<difficulty average="0.1" deviation="0.05" />
<recovery average="0.4" deviation="0.05" />
</natural-resource>
</lw-resources>

View file

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns="http://www.deepclone.com/lw/b6/m2/resources"
targetNamespace="http://www.deepclone.com/lw/b6/m2/resources"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified" attributeFormDefault="unqualified">
<xs:element name="lw-resources">
<xs:complexType>
<xs:sequence>
<xs:element name="basic-resource" type="basic-resource"
minOccurs="0" maxOccurs="unbounded" />
<xs:element name="natural-resource" type="natural-resource"
minOccurs="0" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="basic-resource">
<xs:attribute name="name" use="required" type="xs:token" />
<xs:attribute name="description" use="required" type="xs:token" />
<xs:attribute name="category" use="optional" type="xs:token" />
<xs:attribute name="weight" use="required" type="xs:integer" />
</xs:complexType>
<xs:complexType name="natural-resource">
<xs:complexContent>
<xs:extension base="basic-resource">
<xs:sequence>
<xs:element name="quantity" type="resource-parameter" />
<xs:element name="difficulty" type="resource-parameter" />
<xs:element name="recovery" type="resource-parameter" />
</xs:sequence>
<xs:attribute name="presence-probability" use="required"
type="xs:decimal" />
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="resource-parameter">
<xs:attribute name="average" use="required" type="xs:decimal" />
<xs:attribute name="deviation" use="required" type="xs:decimal" />
</xs:complexType>
</xs:schema>

View file

@ -0,0 +1,372 @@
package com.deepclone.lw.cli;
import java.io.File;
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.ResourceLoader;
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.deepclone.lw.cli.xmlimport.data.resources.UOCResourceErrorCode;
import com.deepclone.lw.utils.StoredProc;
/**
* Resources import tool
*
* <p>
* This class defines the body of the resource import tool. It loads the data file specified on the
* command line, validates it and then loads the resource definitions into the database.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
public class ImportResources
extends CLITool
{
/** Logging system */
private final Logger logger = Logger.getLogger( ImportResources.class );
/** File to read the definitions from */
private File file;
/** Spring transaction template */
private TransactionTemplate tTemplate;
/** Basic resource with category update or create stored procedure */
private StoredProc uocBasicResourceWithCategory;
/** Basic resource without category update or create stored procedure */
private StoredProc uocBasicResourceWithoutCategory;
/** Natural resource with category update or create stored procedure */
private StoredProc uocNaturalResourceWithCategory;
/** Natural resource without category update or create stored procedure */
private StoredProc uocNaturalResourceWithoutCategory;
/**
* Create the Spring context
*
* <p>
* 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/transaction-bean.xml"
} , true , ctx );
}
/**
* Create database access templates
*
* <p>
* Initialise the transaction template and the four stored procedure definitions.
*
* @param ctx
* the Spring application context
*/
private void createTemplates( ApplicationContext ctx )
{
DataSource dSource = ctx.getBean( DataSource.class );
PlatformTransactionManager tManager = ctx.getBean( PlatformTransactionManager.class );
this.uocBasicResourceWithoutCategory = new StoredProc( dSource , "defs" , "uoc_resource" )
.addParameter( "_name" , java.sql.Types.VARCHAR )
.addParameter( "_description" , java.sql.Types.VARCHAR )
.addParameter( "_weight" , java.sql.Types.INTEGER ).addOutput( "_return" , java.sql.Types.OTHER );
this.uocBasicResourceWithCategory = new StoredProc( dSource , "defs" , "uoc_resource" )
.addParameter( "_name" , java.sql.Types.VARCHAR )
.addParameter( "_description" , java.sql.Types.VARCHAR )
.addParameter( "_category" , java.sql.Types.VARCHAR ).addParameter( "_weight" , java.sql.Types.INTEGER )
.addOutput( "_return" , java.sql.Types.OTHER );
this.uocNaturalResourceWithoutCategory = new StoredProc( dSource , "defs" , "uoc_natural_resource" )
.addParameter( "_name" , java.sql.Types.VARCHAR )
.addParameter( "_description" , java.sql.Types.VARCHAR )
.addParameter( "_weight" , java.sql.Types.INTEGER ).addParameter( "_presence" , java.sql.Types.DOUBLE )
.addParameter( "_quantity_avg" , java.sql.Types.DOUBLE )
.addParameter( "_quantity_dev" , java.sql.Types.DOUBLE )
.addParameter( "_difficulty_avg" , java.sql.Types.DOUBLE )
.addParameter( "_difficulty_dev" , java.sql.Types.DOUBLE )
.addParameter( "_recovery_avg" , java.sql.Types.DOUBLE )
.addParameter( "_recovery_dev" , java.sql.Types.DOUBLE ).addOutput( "_return" , java.sql.Types.OTHER );
this.uocNaturalResourceWithCategory = new StoredProc( dSource , "defs" , "uoc_natural_resource" )
.addParameter( "_name" , java.sql.Types.VARCHAR )
.addParameter( "_description" , java.sql.Types.VARCHAR )
.addParameter( "_category" , java.sql.Types.VARCHAR ).addParameter( "_weight" , java.sql.Types.INTEGER )
.addParameter( "_presence" , java.sql.Types.DOUBLE )
.addParameter( "_quantity_avg" , java.sql.Types.DOUBLE )
.addParameter( "_quantity_dev" , java.sql.Types.DOUBLE )
.addParameter( "_difficulty_avg" , java.sql.Types.DOUBLE )
.addParameter( "_difficulty_dev" , java.sql.Types.DOUBLE )
.addParameter( "_recovery_avg" , java.sql.Types.DOUBLE )
.addParameter( "_recovery_dev" , java.sql.Types.DOUBLE ).addOutput( "_return" , java.sql.Types.OTHER );
this.tTemplate = new TransactionTemplate( tManager );
}
/**
* Import all resource definitions
*
* <p>
* Import all resource definitions from the top-level Resources data instance based on the type
* of resource, and whether or not it has a category.
*
* @param data
* the top level Resources data instance
*
* @throws DataImportException
* when some external resource definition fails to load
*/
private void importResources( Resources data )
throws DataImportException
{
for ( BasicResource br : data ) {
if ( br instanceof NaturalResource ) {
if ( br.getCategory( ) == null ) {
this.importNaturalResourceWithoutCategory( (NaturalResource) br );
} else {
this.importNaturalResourceWithCategory( (NaturalResource) br );
}
} else {
if ( br.getCategory( ) == null ) {
this.importBasicResourceWithoutCategory( br );
} else {
this.importBasicResourceWithCategory( br );
}
}
}
}
/**
* Import a Natural Resource
*
* <p>
* Import a natural resource that does not have a defined category.
*
* @param nr
* the resources definition data
*
* @throws DataImportException
* when some external resource definition fails to load
*/
private void importNaturalResourceWithoutCategory( NaturalResource nr )
throws DataImportException
{
UOCResourceErrorCode err = UOCResourceErrorCode.valueOf( ( (PGobject) this.uocNaturalResourceWithoutCategory
.execute( nr.getName( ) , nr.getDescription( ) , nr.getWeight( ) , nr.getPresenceProbability( ) ,
nr.getQuantity( ).getAverage( ) , nr.getQuantity( ).getDeviation( ) ,
nr.getDifficulty( ).getAverage( ) , nr.getDifficulty( ).getDeviation( ) ,
nr.getRecovery( ).getAverage( ) , nr.getRecovery( ).getDeviation( ) ).get( "_return" ) )
.getValue( ) );
if ( !err.equals( UOCResourceErrorCode.CREATED ) && !err.equals( UOCResourceErrorCode.UPDATED ) ) {
throw new DataImportException( "uocNaturalResourceWithoutCategory returned error code " + err.toString( ) );
}
}
/**
* Import a Natural Resource
*
* <p>
* Import a natural resource that does have a defined category.
*
* @param nr
* the resources definition data
*
* @throws DataImportException
* when some external resource definition fails to load
*/
private void importNaturalResourceWithCategory( NaturalResource nr )
throws DataImportException
{
UOCResourceErrorCode err = UOCResourceErrorCode.valueOf( ( (PGobject) this.uocNaturalResourceWithCategory
.execute( nr.getName( ) , nr.getDescription( ) , nr.getCategory( ) , nr.getWeight( ) ,
nr.getPresenceProbability( ) , nr.getQuantity( ).getAverage( ) ,
nr.getQuantity( ).getDeviation( ) , nr.getDifficulty( ).getAverage( ) ,
nr.getDifficulty( ).getDeviation( ) , nr.getRecovery( ).getAverage( ) ,
nr.getRecovery( ).getDeviation( ) ).get( "_return" ) ).getValue( ) );
if ( !err.equals( UOCResourceErrorCode.CREATED ) && !err.equals( UOCResourceErrorCode.UPDATED ) ) {
throw new DataImportException( "uocNaturalResourceWithCategory returned error code " + err.toString( ) );
}
}
/**
* Import a Basic Resource
*
* <p>
* Import a basic resource that does not have a defined category.
*
* @param br
* the resources definition data
*
* @throws DataImportException
* when some external resource definition fails to load
*/
private void importBasicResourceWithoutCategory( BasicResource br )
throws DataImportException
{
UOCResourceErrorCode err = UOCResourceErrorCode.valueOf( ( (PGobject) this.uocBasicResourceWithoutCategory
.execute( br.getName( ) , br.getDescription( ) , br.getWeight( ) ).get( "_return" ) ).getValue( ) );
if ( !err.equals( UOCResourceErrorCode.CREATED ) && !err.equals( UOCResourceErrorCode.UPDATED ) ) {
throw new DataImportException( "uocBasicResourceWithoutCategory returned error code " + err.toString( ) );
}
}
/**
* Import a Basic Resource
*
* <p>
* Import a basic resource that does not have a defined category.
*
* @param br
* the resources definition data
*
* @throws DataImportException
* when some external resource definition fails to load
*/
private void importBasicResourceWithCategory( BasicResource br )
throws DataImportException
{
UOCResourceErrorCode err = UOCResourceErrorCode
.valueOf( ( (PGobject) this.uocBasicResourceWithCategory.execute( br.getName( ) , br.getDescription( ) ,
br.getCategory( ) , br.getWeight( ) ).get( "_return" ) ).getValue( ) );
if ( !err.equals( UOCResourceErrorCode.CREATED ) && !err.equals( UOCResourceErrorCode.UPDATED ) ) {
throw new DataImportException( "uocBasicResourceWithCategory returned error code " + err.toString( ) );
}
}
/**
* Run the resource definitions import tool
*
* <p>
* Loads the data file, the connects to the database and creates or updates all definitions.
*/
@Override
public void run( )
{
Resources data;
try {
data = new ResourceLoader( this.file ).load( );
} catch ( DataImportException e ) {
System.err.println( "Error while loading '" + this.file + "': " + e.getMessage( ) );
this.logger.error( "Error while loading resources data" , e );
return;
}
AbstractApplicationContext ctx = this.createContext( );
this.createTemplates( ctx );
this.executeImportTransaction( data );
ToolBase.destroyContext( ctx );
}
/**
* Execute the resource definitions importation transaction
*
* <p>
* Run a transaction and execute the importation code inside it. Roll back if anything goes
* wrong.
*
* @param data
* the Resources definitions instance
*/
private void executeImportTransaction( final Resources data )
{
boolean rv = this.tTemplate.execute( new TransactionCallback< Boolean >( ) {
@Override
public Boolean doInTransaction( TransactionStatus status )
{
boolean rv = ImportResources.this.doTransaction( data );
if ( !rv ) {
status.setRollbackOnly( );
}
return rv;
}
} );
if ( rv ) {
this.logger.info( "Resource import successful" );
}
}
/**
* Import transaction body
*
* <p>
* Import all definitions and handle exceptions.
*
* @param data
* the Resources definitions instance
* @return
*/
private boolean doTransaction( Resources data )
{
try {
this.importResources( data );
return true;
} catch ( RuntimeException e ) {
this.logger.error( "Caught runtime exception" , e );
} catch ( DataImportException e ) {
this.logger.error( "Error while importing resources" , e );
}
return false;
}
/**
* Obtain the name of the definitions file
*
* <p>
* 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;
}
}

View file

@ -217,12 +217,12 @@ public class ImportText
private boolean doTransaction( I18NText data )
{
try {
ImportText.this.importText( data );
this.importText( data );
return true;
} catch ( RuntimeException e ) {
ImportText.this.logger.error( "Caught runtime exception" , e );
this.logger.error( "Caught runtime exception" , e );
} catch ( DataImportException e ) {
ImportText.this.logger.error( "Error while importing external strings" , e );
this.logger.error( "Error while importing external strings" , e );
}
return false;
}

View file

@ -0,0 +1,118 @@
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.resources.NaturalResource;
import com.deepclone.lw.cli.xmlimport.data.resources.Resources;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.XStreamException;
/**
* Resource Loader
*
* <p>
* This class can be used to load all resource definitions. It extracts them from the XML file and
* verifies them.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
public class ResourceLoader
{
/** The file to read the XML from */
private final File file;
/**
* Initialise the loader
*
* @param file
* the XML file that contains the definitions
*/
public ResourceLoader( File file )
{
this.file = file.getAbsoluteFile( );
}
/**
* Initialise the necessary XStream instance
*
* <p>
* Initialise the XStream instance by processing the annotations of all the resource importable
* data classes.
*
* @return the XStream instance to use when loading the data
*/
private XStream initXStream( )
{
XStream xstream = new XStream( );
xstream.processAnnotations( Resources.class );
xstream.processAnnotations( NaturalResource.class );
return xstream;
}
/**
* Load the resource definitions
*
* <p>
* 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 Resources loadXMLFile( )
throws DataImportException
{
FileInputStream fis;
try {
fis = new FileInputStream( this.file );
} catch ( FileNotFoundException e ) {
throw new DataImportException( "Unable to load resource definitions" , e );
}
try {
try {
XStream xstream = this.initXStream( );
return (Resources) xstream.fromXML( fis );
} finally {
fis.close( );
}
} catch ( IOException e ) {
throw new DataImportException( "Input error while loading resource definitions" , e );
} catch ( XStreamException e ) {
throw new DataImportException( "XML error while loading resource definitions" , e );
}
}
/**
* Load and process resource definitions
*
* <p>
* Attempt to load all resource 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 Resources load( )
throws DataImportException
{
Resources resources = this.loadXMLFile( );
resources.verifyData( );
resources.setReadFrom( this.file );
return resources;
}
}

View file

@ -0,0 +1,105 @@
package com.deepclone.lw.cli.xmlimport.data.resources;
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.XStreamAsAttribute;
/**
* A Basic Resource
*
* <p>
* This class is the base for all resource classes providing a name, description, category and
* weight. In certain cases the category can be null. It provides a method to verify that data has
* been imported successfully.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
@SuppressWarnings( "serial" )
@XStreamAlias( "basic-resource" )
public class BasicResource
extends ImportableData
{
/** The resource's name */
@XStreamAsAttribute
private String name;
/** The resource's description */
@XStreamAsAttribute
private String description;
/**
* The resource's category
*
* <p>
* Can be null for resources that do not belong to a category
*/
@XStreamAsAttribute
private String category;
/**
* The resource's weight
*
* <p>
* Used when sorting resources and resource categories.
*/
@XStreamAsAttribute
private Integer weight; // Is int enough?
/**
* Check a resource's data
*
* <p>
* Make sure that a resource's properties are both present and valid.
*/
@Override
public void verifyData( )
throws DataImportException
{
if ( this.name == null || "".equals( this.name.trim( ) ) ) {
throw new DataImportException( "Missing name string" );
}
if ( this.description == null || "".equals( this.description.trim( ) ) ) {
throw new DataImportException( "Missing name string" );
}
if ( this.category != null && "".equals( this.description.trim( ) ) ) {
throw new DataImportException( "Category invalid" );
}
if ( this.weight == null ) {
throw new DataImportException( "Missing weight" );
}
}
/** @return the resource's name */
public String getName( )
{
return this.name;
}
/** @return the resource's description */
public String getDescription( )
{
return this.description;
}
/** @return the resource's category */
public String getCategory( )
{
return this.category;
}
/** @return the resource's weight */
public Integer getWeight( )
{
return this.weight;
}
}

View file

@ -0,0 +1,119 @@
package com.deepclone.lw.cli.xmlimport.data.resources;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
/**
* A Natural Resource
*
* <p>
* This class represents naturally occurring resources. As well as extending {@link BasicResource}
* it contains the presence-probability, quantity, difficulty and recovery of a resource and a
* method to verify the data of these properties.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
@SuppressWarnings( "serial" )
@XStreamAlias( "natural-resource" )
public class NaturalResource
extends BasicResource
{
/**
* The resource's presence-probability
*
* <p>
* Used by the universe generator to decide whether to add this type of resource to a particular
* planet or not.
*/
@XStreamAlias( "presence-probability" )
@XStreamAsAttribute
private Double presenceProbability;
/** The resource's quantity */
@XStreamAlias( "quantity" )
private ResourceParameter quantity;
/**
* The resource's difficulty
*
* <p>
* The difficulty of extracting the resource.
*/
@XStreamAlias( "difficulty" )
private ResourceParameter difficulty;
/**
* The resource's recovery
*
* <p>
* The recovery rate of the resource.
*/
@XStreamAlias( "recovery" )
private ResourceParameter recovery;
/**
* Check a resource's data
*
* <p>
* Make sure that a resource's properties are both present and valid. Calls the base class'
* verifyData() method to check all of the data.
*/
@Override
public void verifyData( )
throws DataImportException
{
super.verifyData( );
if ( this.presenceProbability == null ) {
throw new DataImportException( "Missing presence-probability" );
}
if ( this.quantity == null ) {
throw new DataImportException( "Missing quantity" );
}
this.quantity.verifyData( "quantity" );
if ( this.difficulty == null ) {
throw new DataImportException( "Missing difficulty" );
}
this.difficulty.verifyData( "difficulty" );
if ( this.recovery == null ) {
throw new DataImportException( "Missing recovery" );
}
this.recovery.verifyData( "recovery" );
}
/** @return the resource's presence-probability */
public Double getPresenceProbability( )
{
return this.presenceProbability;
}
/** @return the resource's quantity */
public ResourceParameter getQuantity( )
{
return this.quantity;
}
/** @return the resource's difficulty */
public ResourceParameter getDifficulty( )
{
return this.difficulty;
}
/** @return the resource's recovery */
public ResourceParameter getRecovery( )
{
return this.recovery;
}
}

View file

@ -0,0 +1,65 @@
package com.deepclone.lw.cli.xmlimport.data.resources;
import com.deepclone.lw.cli.xmlimport.data.DataImportException;
import com.deepclone.lw.cli.xmlimport.data.ImportableData;
import com.thoughtworks.xstream.annotations.XStreamAsAttribute;
/**
* Resource property parameter
*
* <p>
* This class represents the average and deviation of various properties of a resource.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
@SuppressWarnings( "serial" )
public class ResourceParameter
extends ImportableData
{
/** The property's average */
@XStreamAsAttribute
private Double average;
/** The property's deviation */
@XStreamAsAttribute
private Double deviation;
/**
* Check the property's data
*
* <p>
* Make sure that a average and deviation are present.
*
* @param property
* the name of the property
*/
public void verifyData( String property )
throws DataImportException
{
if ( this.average == null ) {
throw new DataImportException( "Missing average for " + property );
}
if ( this.deviation == null ) {
throw new DataImportException( "Missing deviation for " + property );
}
}
/** @return the property's average */
public Double getAverage( )
{
return this.average;
}
/** @return the property's deviation */
public Double getDeviation( )
{
return this.deviation;
}
}

View file

@ -0,0 +1,96 @@
package com.deepclone.lw.cli.xmlimport.data.resources;
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;
/**
* Resource Data
*
* <p>
* This class represents the contents of the resource text file. It contains a list of resource
* definitions.
*
* @author <a href="mailto:tim@mitheren.com">T. Rosser</a>
*/
@SuppressWarnings( "serial" )
@XStreamAlias( "lw-resources" )
public class Resources
extends ImportableData
implements Iterable< BasicResource >
{
/** All present resource definitions */
@XStreamImplicit
private final List< BasicResource > resources = new LinkedList< BasicResource >( );
/**
* Checks the resource data
*
* <p>
* Checks each definition and ensures they are all unique.
*/
@Override
public void verifyData( )
throws DataImportException
{
if ( this.resources == null ) {
throw new DataImportException( "No resource definitions" );
}
HashSet< String > names = new HashSet< String >( );
for ( BasicResource resource : this.resources ) {
resource.verifyData( );
this.checkUniqueItem( names , resource.getName( ) );
}
}
/**
* Checks that the name of the resource is unique
*
* <p>
* This helper method is used by {@link #verifyData()} to make sure resource names are unique.
*
* @param existing
* the existing set of items
* @param value
* the item's value
*
* @throws DataImportException
* if the item's value is already present in the set of existing items
*/
public void checkUniqueItem( HashSet< String > existing , String value )
throws DataImportException
{
if ( existing.contains( value ) ) {
throw new DataImportException( "Duplicate resource name '" + value + "'" );
}
existing.add( value );
}
/**
* Resource definition iterator
*
* <p>
* Grant access to the list of resources in read-only mode.
*
* @return a read-only iterator on the list of resources.
*/
@Override
public Iterator< BasicResource > iterator( )
{
return Collections.unmodifiableList( this.resources ).iterator( );
}
}

View file

@ -0,0 +1,20 @@
package com.deepclone.lw.cli.xmlimport.data.resources;
import com.deepclone.lw.cli.ImportResources;
/**
* Enum representing the error codes returned by the uoc_* stored procedures in
* {@link ImportResources}
*
* @author <a href="tim@mitheren.com">T. Rosser</a>
*
*/
public enum UOCResourceErrorCode {
CREATED ,
UPDATED ,
BAD_TYPE ,
BAD_STRINGS ,
BAD_VALUE ,
DUP_DESCR;
}