package votorola.a.election; // Copyright 2007-2008, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
import java.io.*;
import java.sql.*;
import javax.xml.stream.*;
import votorola._.*;
import votorola.a.*;
import votorola.a.voter.*;
import votorola.g.lang.*;
import votorola.g.logging.*;
/** A voter's input to an election, backed by a row of the election's voter input table.
*/
public @ThreadRestricted("touch") final class Vote implements Cloneable, Serializable
{
// cf. votorola.a.register.RegistrationC
private static final long serialVersionUID = 0L;
/** Constructs a Vote, reading its initial state from
* the voter input table.
*
* @param voterEmail per {@linkplain #voterEmail() voterEmail}()
* @param voterInputTable of the election
*/
public Vote( final String voterEmail, final VoterInputTable voterInputTable )
throws SQLException
{
this( voterEmail );
assert voterInputTable.electoralService() instanceof Election;
final String xml = voterInputTable.get( voterEmail );
if( xml == null ) LoggerX.i(getClass()).finest( "stored input is null, assuming defaults: " + voterEmail );
else
{
try
{
init( xml );
}
catch( XMLStreamException x ) { throw voterInputTable.newUnparseableInputException( voterEmail, xml, x ); }
}
}
/** Constructs a Vote from the specified initial data.
*
* @param voterEmail per {@linkplain #voterEmail() voterEmail}()
* @param xml initial data from the '{@linkplain VoterInputTable#get(String) xml}'
* column of the voter input table, or null to use defaults
*/
Vote( String voterEmail, String xml ) throws XMLStreamException
{
this( voterEmail );
if( xml != null ) init( xml );
}
/** Constructs a Vote with default initial data.
*
* @param voterEmail per {@linkplain #voterEmail() voterEmail}()
*/
Vote( String voterEmail ) { this.voterEmail = voterEmail; }
private final void init( final String xml ) throws XMLStreamException // not dynamically bound when called from constructor/initializer
{
final StringReader sR = new StringReader( xml );
try
{
final XMLStreamReader xmlR = VoterInputTable.createXMLStreamReader( sR );
try
{
while( xmlR.hasNext() )
{
xmlR.next();
if( xmlR.isStartElement() && "X".equals( xmlR.getLocalName() ))
{
candidateEmail = xmlR.getAttributeValue( /*namespaceURI*/null, "c" );
break;
}
}
}
finally{ xmlR.close(); }
}
finally{ sR.close(); }
}
// ------------------------------------------------------------------------------------
/** Writes this voter input to the table, if it has uncommitted changes;
* or removes it, if it is at default.
*
* @param voterInputTable of the election
*/
void commit( VoterInputTable voterInputTable, ElectoralSubserver.UserSession userSession )
throws SQLException, VoterInputTable.BadInputException
{
commit( voterInputTable, userSession, /*toForce*/false );
}
/** Writes this voter input to the table; or removes it, if it is at default.
*
* @param voterInputTable of the election
* @param toForce false to commit only if changes were made in this instance
* of voter input; true to force the commit, regardless
*/
void commit( final VoterInputTable voterInputTable,
final ElectoralSubserver.UserSession userSession, final boolean toForce )
throws SQLException, VoterInputTable.BadInputException
{
if( !( toForce || isChanged )) return;
final StringBuilder xmlB = new StringBuilder();
VoterInputTable.appendField( "c", candidateEmail, xmlB );
isChanged = false; // early, in case clobbering another thread's true - isChanged must be volatile for this
try
{
if( xmlB.length() == 0 )
{
LoggerX.i(getClass()).finest( "removing storage, all data is at default" );
voterInputTable.remove( voterEmail );
}
else
{
// xmlB.insert( 0, "" );
xmlB.insert( 0, "" );
voterInputTable.put( voterEmail, xmlB.toString(), userSession );
}
}
catch( RuntimeException x ) { isChanged = true; throw x; } // rollback
catch( SQLException x ) { isChanged = true; throw x; }
}
/** Identifies the candidate for whom the voter is voting.
*
* @return canonical email address (having no personal part);
* or null, if the voter's vote is currently uncast
*
* @see #setCandidateEmail(String)
*/
public String getCandidateEmail() { return candidateEmail; } // must be public, or Wicket 1.3.2 will directly access private field
private String candidateEmail;
/** Changes the candidate for whom the voter is voting.
*
* @see #getCandidateEmail()
*/
public void setCandidateEmail( String newCandidateEmail ) // must be public, ditto
{
if( ObjectX.nullEquals( newCandidateEmail, candidateEmail )) return;
candidateEmail = newCandidateEmail;
isChanged = true;
}
/** Identifies the voter.
*
* @return canonical email address (having no personal part)
*/
String voterEmail() { return voterEmail; }
private final String voterEmail;
// - O b j e c t ----------------------------------------------------------------------
public final @Override Vote clone()
{
try
{
return (Vote)super.clone();
}
catch( CloneNotSupportedException x ) { throw new RuntimeException( x ); } // never occurs
}
// ====================================================================================
/** An event that records the casting of a vote.
*/
public static final class CastEvent extends VotingEvent
{
/** Creates a CastEvent.
*/
CastEvent( String electionName, Vote vote ) { super( electionName, vote ); }
}
// ====================================================================================
/** An event that records the casting or withdrawal of a vote.
*/
public static class VotingEvent extends ActivityEvent
{
private static final long serialVersionUID = 0L;
private VotingEvent( String electionName, Vote vote )
{
this.electionName = electionName;
candidateEmail = vote.getCandidateEmail();
voterEmail = vote.voterEmail();
}
// --------------------------------------------------------------------------------
/** Identifies the candidate for whom the vote was cast,
* or from whom the vote was withdrawn.
*
* @see Vote#getCandidateEmail()
*/
public String candidateEmail() { return candidateEmail; }
private final String candidateEmail;
/** Names the election in which the vote was cast or withdrawn.
*
* @see Election#name()
*/
public String electionName() { return electionName; }
private final String electionName;
/** Identifies the voter.
*
* @see Vote#voterEmail()
*/
public String voterEmail() { return voterEmail; }
private final String voterEmail;
}
// ====================================================================================
/** An event that records the withdrawal of a vote.
*/
public static final class WithdrawalEvent extends VotingEvent
{
/** Creates a WithdrawalEvent.
*/
WithdrawalEvent( String electionName, Vote vote ) { super( electionName, vote ); }
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
private volatile boolean isChanged; // volatile per commit()
}