package votorola.a.election; // Copyright 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.util.*;
import org.apache.wicket.*;
import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.basic.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.protocol.http.*;
import votorola._.*;
import votorola.a.*;
import votorola.a.election.district.*;
import votorola.a.locale.*;
import votorola.a.web.*;
import votorola.g.lang.*;
import votorola.g.servlet.*;
import votorola.g.util.*;
/** An election overview page. The particular election is specified by query parameter
* 's'. Query parameters for this page are:
*
*
*
* | Key |
* Value |
* Default |
*
*
* | s |
*
* {@linkplain Election#name() Service name} of the election.
* |
*
* Null, leaving the page empty, except for the navigation controls.
* |
*
*
*
* @see
* WP_Election.html
*/
@ThreadRestricted("wicket")
public final class WP_Election extends VPage implements TabbedPage, RegionalPage // public - bookmarkable page
{
/** Constructs a WP_Election.
*/
public WP_Election( final PageParameters parameters ) // public - bookmarkable page, must have default|PageParameter constructor
{
super( parameters );
final VRequestCycle cycle = VRequestCycle.get();
eFet = new WP_Election.ElectionFetcher( getPageParameters(), cycle );
final RegionalPathMPE regionalPathMPE = // first, as it writes path to session
new RegionalPathMPE( eFet.election(), WP_Election.this, cycle );
add( new WC_NavigationControls( "navSuperBar", WP_Election.this, cycle ));
add( new ElectionNavPile( "navPile", navTab(cycle), cycle, regionalPathMPE ));
regionalPath = RegionalPath1.newPath( regionalPathMPE ); // immutable
init_content( cycle );
}
private void init_content( final VRequestCycle cycle )
{
final BundleFormatter bunW = cycle.bunW();
if( eFet.election() == null )
{
add( new Label( "title", bunW.l( "a.election.navPath.promptTitle" )));
add( newNullComponentAsLabel( "content" ));
return;
}
final Fragment content = new Fragment( "content", "singlefrag-content", WP_Election.this );
add( content );
// Title and Summary Description
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{
final String title = eFet.election().title();
add( new Label( "title", title ));
content.add( new Label( "titleH", title ));
content.add( new Label( "summaryDescription", eFet.election().summaryDescription() ));
}
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
final Fragment main;
if( eFet.countToReport() == null )
{
main = new Fragment( "main-content", "frag-main-content-no-count", WP_Election.this );
main.add( new Label( "explanation", bunW.l( "a.election.noResultsToReport" )));
content.add( main );
return;
}
final BundleFormatter bunA = cycle.bunA();
main = new Fragment( "main-content", "singlefrag-main-content-normal", WP_Election.this );
content.add( main );
// Summary table
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
{
final long electorateSize = eFet.election().electorateSize();
main.add( new Label( "electorateSize-index",
bunA.l( "a.election.Count.summaryData.electorateSize-index" )));
main.add( new Label( "electorateSize-datum", electorateSize > 0?
bunA.format( "%,d", electorateSize ):
bunA.l( "a.quantityUnknown" )));
main.add( new Label( "electorateSize-note",
eFet.election().electorateSizeExplanation() ));
main.add( new Label( "singleCastCount-index",
bunA.l( "a.election.Count.summaryData.singleCastCount-index" )));
main.add( new Label( "singleCastCount-datum",
bunA.format( "%,d", eFet.countToReport().singleCastCount() )));
main.add( new Label( "singleCastCount-note",
bunA.l( "a.election.Count.summaryData.singleCastCount-note" )));
main.add( new Label( "turnoutPercent-index",
bunA.l( "a.election.Count.summaryData.turnoutPercent-index" )));
main.add( new Label( "turnoutPercent-datum", electorateSize > 0?
bunA.format( "%.3f",
eFet.countToReport().singleCastCount() * 100d / electorateSize ):
bunA.l( "a.quantityUnknown" )));
main.add( new Label( "turnoutPercent-note",
bunA.l( "a.election.Count.summaryData.turnoutPercent-note" )));
}
// Count identifier
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
main.add( new Label( "count-identifier",
eFet.countToReport().readyDirectory().toUIString() ));
}
// ````````````````````````````````````````````````````````````````````````````````````
// Initialized early, for use in other initializers.
/** The left-hand navigation tab in the election bar, linking to the election overview
* page (an instance of WP_Election).
*/
private static final NavTab NAV_TAB = new ElectionTab( WP_Election.class )
{
public String shortTitle( VRequestCycle cycle )
{
return cycle.bunW().l( "a.election.WP_Election.tab.shortTitle" );
}
};
// ------------------------------------------------------------------------------------
/** Returns the election corresponding to the specified election name.
*
* @return election; or null, if the election name is null
*
* @throws RestartResponseException redirecting to an error message page,
* if the election name does not identify a known election
*/
private static Election electionOrNullFor( final String electionName,
final VRequestCycle cycle )
{
if( electionName == null ) return null;
final ElectoralService service =
cycle.vApplication().subserverRun().electoralService( electionName );
if( service == null )
{
throw new RestartResponseException(
WP_Regional.newUnrecognizedNodeMessage( electionName, cycle.bunW() ));
}
if( !( service instanceof Election ))
{
cycle.vSession().error( "not an election: " + electionName );
throw new RestartResponseException( new WP_Message() );
}
return (Election)service;
}
/** Returns the election corresponding to query parameter 's', if it is specified.
*
* @return election; or null, if no query parameter 's' is specified
*
* @throws RestartResponseException redirecting to an error message page,
* if the specified parameter 's' does not identify a known election
*/
static Election electionOrNullFor( final PageParameters parameters, final VRequestCycle cycle )
{
return electionOrNullFor( parameters.getString("s"), cycle );
}
/** The bar for navigating among the pages of the election.
*/
public static NavBar navBar() { return navBar; }
private static final NavBar navBar = new NavBar()
{
public SuperTab superTab() { return superTab; } // "Election"
private final SuperTab superTab = new SuperTab( this )
{
public String shortTitle( VRequestCycle cycle )
{
return cycle.bunW().l( "a.election.WP_Election.superTab.shortTitle" );
}
};
public List tabList() { return tabList; }
private final ArrayListU tabList = new ArrayListU( new NavTab[]
{
NAV_TAB.setNavBar( this ),
WP_Vote.NAV_TAB.setNavBar( this ),
WP_Count.NAV_TAB.setNavBar( this )
});
};
// - R e g i o n a l - P a g e --------------------------------------------------------
public RegionalPath regionalPath() { return regionalPath; }
private final RegionalPath regionalPath;
// - T a b b e d - P a g e ------------------------------------------------------------
/** @see #NAV_TAB
*/
public NavTab navTab( VRequestCycle cycle ) { return NAV_TAB; }
// ====================================================================================
/** A serializeable container for an election and its latest count.
*/
@ThreadRestricted("wicket") static final class ElectionFetcher implements Serializable
{
private static final long serialVersionUID = 0L;
ElectionFetcher( final PageParameters parameters, final VRequestCycle cycle )
{
electionName = parameters.getString( "s" );
election( cycle ); // in anticipation, taking advantage of the cycle reference
}
private final String electionName;
// --------------------------------------------------------------------------------
public Count countToReport()
{
if( countOrNull == null && !"".equals(count_readyDirectoryPath) ) // for a newly contructed 'this', or deserialized one that had a count
{
election().lock().lock();
try { countOrNull = election().countToReport(); }
catch( IOException x ) { throw new RuntimeException( x ); } // not much expected
finally { election().lock().unlock(); }
final String path;
if( countOrNull == null ) path = "";
else path = countOrNull.readyDirectory().getPath();
if( count_readyDirectoryPath == null ) count_readyDirectoryPath = path;
else if( !count_readyDirectoryPath.equals( path ))
{
throw new VotorolaRuntimeException(
"The election has been recounted since this page was constructed. FIX this to show the user a WP_Message, with a link to new page." );
}
}
if( countOrNull != null && !countOrNull.readyDirectory().isMounted() )
{
throw new VotorolaRuntimeException(
"The election count has been unmounted since this page was constructed. FIX this to show the user a WP_Message, with a link to new page." );
}
return countOrNull;
}
private transient Count countOrNull;
private String count_readyDirectoryPath;
public Election election() { return election( VRequestCycle.get() ); }
public Election election( final VRequestCycle cycle )
{
if( electionOrNull == null && electionName != null )
{
electionOrNull = electionOrNullFor( electionName, cycle ); // for a newly contructed or deserialized 'this'
}
return electionOrNull;
}
private transient Election electionOrNull;
}
// ====================================================================================
/** A link to a page under the election navigation bar. The election is specified in
* advance. But the page type is set, per click, as the last type that was viewed.
*
* @see #navBar()
* @see NavBar#lastRunner(VRequestCycle)
*/
public static final class ElectionLink extends LinkX
{
/** Constructs an ElectionLink.
*/
public ElectionLink( String id, RegionalElectionNode regionalNode )
{
super( id );
this.regionalNode = regionalNode;
}
private final RegionalElectionNode regionalNode;
// - L i n k ----------------------------------------------------------------------
public @Override void onClick()
{
final VRequestCycle cycle = VRequestCycle.get();
final ElectionTab.Runner electionTabRunner;
{
final Page page = getPage();
final ElectionTab electionTab = ElectionTab.ofPage( page, cycle );
if( electionTab != null ) electionTabRunner = electionTab.runner( cycle );
else // current page has no election tab
{
final RequestCycleRunner runner = navBar.lastRunner( cycle );
if( runner instanceof ElectionTab.Runner )
{
electionTabRunner = (ElectionTab.Runner)runner;
}
else
{
assert false: "all pages in the election nav-bar have election tabs";
runner.run( cycle );
return;
}
}
}
electionTabRunner.run( regionalNode, cycle );
}
}
// ====================================================================================
/** Navigation bars for the top of an election page.
*/
static final class ElectionNavPile extends WC_NavPile
{
/** Constructs an ElectionNavPile.
*
* @see WC_NavPile#WC_NavPile(String,NavTab,VRequestCycle)
*/
ElectionNavPile( String id, NavTab pageTab, VRequestCycle cycle,
final RegionalPathMP regionalPath )
{
super( id );
this.regionalPath = regionalPath;
init( pageTab, cycle );
}
private final RegionalPathMP regionalPath;
// - W C - N a v - P i l e -------------------------------------------------------
protected Component newVBarLeader( final String id, final NavBar bar )
{
return bar == navBar?
new WC_RegionalPathNavigator( id, regionalPath, RegionalStratum.election,
VRequestCycle.get() ):
VPage.newNullComponent( id );
}
}
// ====================================================================================
/** A navigation tab for an election page.
*/
static abstract @ThreadSafe class ElectionTab extends NavTab
{
/** Contructs an ElectionTab.
*/
ElectionTab( Class pageClass ) { runner = new Runner( pageClass ); }
private static ElectionTab ofPage( final Page page, final VRequestCycle cycle )
{
if( !( page instanceof TabbedPage )) { return null; }
final NavTab tab = ((TabbedPage)page).navTab( cycle );
if( !( tab instanceof ElectionTab )) { return null; }
return (ElectionTab)tab;
}
private final Runner runner;
// - N a v - T a b ----------------------------------------------------------------
/** @throws UnsupportedOperationException
*/
public final Bookmark bookmark() { throw new UnsupportedOperationException(); }
public @Override final Class pageClass() { return runner.pageClass; }
public @Override final Runner runner( VRequestCycle c ) { return runner; }
// ================================================================================
private static final class Runner implements RequestCycleRunner
{
private Runner( Class pageClass ) { this.pageClass = pageClass; }
private final Class pageClass;
private void run( RegionalElectionNode e, VRequestCycle cycle )
{
final PageParameters parameters = new PageParameters();
if( e != null ) parameters.put( "s", e.name() );
cycle.setResponsePage( pageClass, parameters );
}
// - R e q u e s t - C y c l e - R u n n e r ----------------------------------
public void run( WebRequestCycle cW )
{
final VRequestCycle cycle = (VRequestCycle)cW;
final RegionalElectionNode e = (RegionalElectionNode)
cycle.vSession().scopeRegionalPathMP().lastNodeDisplayed(
RegionalStratum.election, /*correctForCurrent*/true, cycle );
run( e, cycle );
}
}
}
// ====================================================================================
/** A mutable regional path for an election page.
*/
@ThreadRestricted("wicket") static final class RegionalPathMPE extends RegionalPathMP
{
private static final long serialVersionUID = 0L;
public RegionalPathMPE( final Election election, final VPage page,
final VRequestCycle cycle )
{
super( election == null? null: election.regionalNode(), RegionalStratum.election,
/*sub-single, though it has no effect*/true, page, cycle );
isContentDisplayed = election != null;
}
private final boolean isContentDisplayed;
// - R e g i o n a l - P a t h - M ------------------------------------------------
public void regionalPathChanged()
{
super.regionalPathChanged();
final RegionalElectionNode e = getElection();
if( e != null ) // new page, to show new content
{
final PageParameters newParameters =
PageParametersX.copyOrNew( page.getPageParameters() );
newParameters.put( "s", e.name() );
page.vRequestCycle().setResponsePage( page.getClass(), newParameters );
return;
}
if( isContentDisplayed ) // new page, to show no content
{
final PageParameters newParameters =
PageParametersX.copyOrNew( page.getPageParameters() );
newParameters.remove( "s" );
page.vRequestCycle().setResponsePage( page.getClass(), newParameters );
return;
}
}
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
private final WP_Election.ElectionFetcher eFet;
}