package votorola.s.wic.count; // Copyright 2009-2013, 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.util.*; import votorola.a.count.*; import votorola.g.lang.*; import votorola.g.util.*; import static votorola.a.count.XCastRelation.CO_VOTER; import static votorola.a.count.XCastRelation.VOTER; /** A tier in a votespace page. There are three types of tier: upstream, base, and * orphan. All are comprised of count nodes. In an upstream tier, the nodes represent * co-voters who are voting for the same candidate. In a base tier, the nodes represent * base candidates who are voting for nobody, or (in the case of cyclers) for themselves. * Finally, an orphan tier represents a single non-participant, one who is neither a * voter nor a candidate. * *

The rows of the tier are indexed from zero (top), and ordered as * follows:

*/ final @ThreadRestricted("wicket") class Tier { /** Constructs an orphan tier. */ Tier( CountNodeW _pathNode, CountNodeW node ) { this( _pathNode, null, new ArrayListU(new CountNodeW[] { node }), null, null ); } /** Constructs a tier from an ArrayList. * * @param candidateNode the count node of the candidate in the downstream tier, * or null if this is a base or orphan tier. */ Tier( CountNodeW pathNode, CountNodeW crosspathNode, ArrayList properNodesList, CountNodeW candidateNode, Count count ) { this( pathNode, crosspathNode, new ArrayListU( properNodesList.toArray( new CountNodeW[properNodesList.size()] )), candidateNode, count ); } /** @param count the count, or null if this is an orphan tier. */ private Tier( CountNodeW _pathNode, CountNodeW _crosspathNode, ArrayListU _properNodesList, final CountNodeW candidateNode, final Count count ) { pathNode = _pathNode; crosspathNode = _crosspathNode; properNodesList = _properNodesList; int r = 0; properRow = r; r += properNodesList.size(); final int pathRowProper; final boolean pathNodeIsSeparate; if( pathNode == null ) { pathRowProper = -1; pathNodeIsSeparate = false; } else { pathRowProper = properNodesList.indexOf( pathNode ); pathNodeIsSeparate = pathRowProper == -1; } final int crosspathRowProper; final boolean crosspathNodeIsSeparate; if( crosspathNode == null ) { crosspathRowProper = -1; crosspathNodeIsSeparate = false; } else if( crosspathNode.equals( pathNode )) { crosspathRowProper = pathRowProper; crosspathNodeIsSeparate = false; } else { crosspathRowProper = properNodesList.indexOf( crosspathNode ); crosspathNodeIsSeparate = crosspathRowProper == -1; } { CountCumulate c; if( count == null ) c = null; else { c = new CountCumulate(); if( candidateNode == null ) // base candidate tier { c.outflowVolume = -1; // not calculated if( count.candidateCount() == 0 ) c.holdVolume = CountCumulate.NO_PARTICIPANTS; else if( count.baseCandidateCount() > properNodesList.size() ) { c.holdVolume = count.holdVolume(); if( c.holdVolume > 0 ) { for( CountNodeW node: properNodesList ) c.holdVolume -= node.holdVolume(); if( pathNodeIsSeparate ) c.holdVolume -= pathNode.holdVolume(); if( crosspathNodeIsSeparate ) c.holdVolume -= crosspathNode.holdVolume(); } } else c = null; // no other nodes } else // voter tier { c.outflowVolume = candidateNode.receiveVolume(); c.holdVolume = -1; // not calculated for( CountNodeW node: properNodesList ) { c.outflowVolume -= node.castVolume(); c.outflowVolume -= node.carryVolume(); } if( pathNodeIsSeparate ) { c.outflowVolume -= pathNode.castVolume(); c.outflowVolume -= pathNode.carryVolume(); } if( crosspathNodeIsSeparate ) { c.outflowVolume -= crosspathNode.castVolume(); c.outflowVolume -= crosspathNode.carryVolume(); } if( c.outflowVolume == 0 ) c = null; // no other nodes } } otherNodesCumulate = c; } otherNodesRow = otherNodesCumulate == null? -1: r++; pathRow = pathNodeIsSeparate? r++: pathRowProper; crosspathRow = crosspathNodeIsSeparate? r++: crosspathRowProper; if( count == null ) sumRow = -1; // none for orphan tier else { sumRow = r; r += 2; /* 2 to match its double rowspan, which gives room for its borders etc., so it doesn't affect the layout of other tiers */ } footnoteRow = r; } // -------------------------------------------------------------------------------- /** The crosspath node, or null if there is none. This corresponds to the node on the * user's own vote path where it crosses the tier. The crosspath node may be in or * out of the proper node list, and equal or unequal to the path node. */ final CountNodeW crosspathNode; /** The row of the crosspath node, or -1 if there is no crosspath node. */ final int crosspathRow; final WP_Votespace.FootnoteBuilder footnoteBuilder = new WP_Votespace.FootnoteBuilder(); /** The first footnote row. */ final int footnoteRow; /** Returns the node for the specified row, which is either a proper node * or a path node (or crosspath node) external to the proper list. */ CountNodeW getNode( final int r ) { final CountNodeW node; if( r == crosspathRow ) node = crosspathNode; else if( r == pathRow ) node = pathNode; else node = properNodesList.get( r ); return node; } /** The row of the last node. It is either a proper node, other node, or external * path or crosspath node. */ int lastNodeRow() { final int followingRow = sumRow == -1? footnoteRow: sumRow; return followingRow - 1; } /** The cumulative count of the other nodes, not individually shown in the * view, or null if there are no other nodes. */ final CountCumulate otherNodesCumulate; /** The row of the other-nodes cumulate, or -1 if there are no other nodes. */ final int otherNodesRow; /** The path node, or null if there is none. This path node may be in or out of the * proper node list, and equal or unequal to the crosspath node. */ final CountNodeW pathNode; /** The row of the path node, or -1 if there is no path node. */ final int pathRow; /** The corresponding {@linkplain votorola.s.gwt.stage.vote.MajorV#place() place} in * the vote track, or {@linkplain votorola.a.count.XCastRelation#UNKNOWN UNKNOWN} if * there is none. */ XCastRelation place = XCastRelation.UNKNOWN; /** @param t the ordinal of this tier, counting from 0 (leftmost). * @param tN the number of tiers. */ void initPlace( final CountNodeW specificPathNode, final int t, final int tN ) { if( tN == 1 ) place = CO_VOTER; // only one tier, they are base co-candidates else if( specificPathNode.isCandidate() ) { if( t == 0 ) place = VOTER; // first tier is voters else if( t == 1 ) place = CO_VOTER; // second is co-voters or base co-candidates } else // no voters left of specific node, no voter board in vote track { if( !specificPathNode.isVoter() ) // non-participant, orphan { if( t == 1 ) place = CO_VOTER; // second tier is co-voters or base co-candidates } else if( t == 0 ) place = CO_VOTER; // first tier is co-voters or base co-candidates } } /** An unmodifiable list of the proper nodes in this tier. */ final List properNodesList; /** The row of the first proper node. */ final int properRow; /** The total number of rows. */ int rowCount() { return footnoteRow + 1; } /** The first of the two sum rows, or -1 if there are none. */ final int sumRow; // ==================================================================================== /** A total of counts across multiple count nodes. */ static final class CountCumulate { /** The total of hold counts, or NO_PARTICIPANTS. This is not calculated (is -1) * for non-base candidates. * * @see CountNodeW#holdVolume() */ long holdVolume; /** The negative value of holdVolume that signals a poll without participants. */ static final long NO_PARTICIPANTS = -2L; /** The total of combined cast and carry volume. This is not calculated (is -1) * for base candidates. * * @see CountNodeW#castVolume() * @see CountNodeW#carryVolume() */ long outflowVolume; } }