001package 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. 002 003import java.util.*; 004import votorola.a.count.*; 005import votorola.g.lang.*; 006import votorola.g.util.*; 007 008import static votorola.a.count.XCastRelation.CO_VOTER; 009import static votorola.a.count.XCastRelation.VOTER; 010 011 012/** A tier in a votespace page. There are three types of tier: upstream, base, and 013 * orphan. All are comprised of count nodes. In an upstream tier, the nodes represent 014 * co-voters who are voting for the same candidate. In a base tier, the nodes represent 015 * base candidates who are voting for nobody, or (in the case of cyclers) for themselves. 016 * Finally, an orphan tier represents a single non-participant, one who is neither a 017 * voter nor a candidate. 018 * 019 * <p>The rows of the tier are indexed from zero (top), and ordered as 020 * follows:</p><ul> 021 * 022 * <li>{@linkplain #properRow properRow} (first and subsequent)</li> 023 * <li>{@linkplain #otherNodesRow otherNodesRow} (if any)</li> 024 * <li>{@linkplain #pathRow pathRow} (if not a proper node)</li> 025 * <li>{@linkplain #crosspathRow crosspathRow} (if not a proper node)</li> 026 * <li>{@linkplain #sumRow sumRow}</li> 027 * <li>{@linkplain #footnoteRow footnoteRow} (first and subsequent)</li> 028 * 029 * </ul> 030 */ 031final @ThreadRestricted("wicket") class Tier 032{ 033 034 /** Constructs an orphan tier. 035 */ 036 Tier( CountNodeW _pathNode, CountNodeW node ) 037 { 038 this( _pathNode, null, new ArrayListU<CountNodeW>(new CountNodeW[] { node }), 039 null, null ); 040 } 041 042 043 044 /** Constructs a tier from an ArrayList. 045 * 046 * @param candidateNode the count node of the candidate in the downstream tier, 047 * or null if this is a base or orphan tier. 048 */ 049 Tier( CountNodeW pathNode, CountNodeW crosspathNode, 050 ArrayList<CountNodeW> properNodesList, CountNodeW candidateNode, Count count ) 051 { 052 this( pathNode, crosspathNode, new ArrayListU<CountNodeW>( 053 properNodesList.toArray( new CountNodeW[properNodesList.size()] )), 054 candidateNode, count ); 055 } 056 057 058 059 /** @param count the count, or null if this is an orphan tier. 060 */ 061 private Tier( CountNodeW _pathNode, CountNodeW _crosspathNode, 062 ArrayListU<CountNodeW> _properNodesList, final CountNodeW candidateNode, final Count count ) 063 { 064 pathNode = _pathNode; 065 crosspathNode = _crosspathNode; 066 properNodesList = _properNodesList; 067 068 int r = 0; 069 properRow = r; r += properNodesList.size(); 070 final int pathRowProper; 071 final boolean pathNodeIsSeparate; 072 if( pathNode == null ) 073 { 074 pathRowProper = -1; 075 pathNodeIsSeparate = false; 076 } 077 else 078 { 079 pathRowProper = properNodesList.indexOf( pathNode ); 080 pathNodeIsSeparate = pathRowProper == -1; 081 } 082 083 final int crosspathRowProper; 084 final boolean crosspathNodeIsSeparate; 085 if( crosspathNode == null ) 086 { 087 crosspathRowProper = -1; 088 crosspathNodeIsSeparate = false; 089 } 090 else if( crosspathNode.equals( pathNode )) 091 { 092 crosspathRowProper = pathRowProper; 093 crosspathNodeIsSeparate = false; 094 } 095 else 096 { 097 crosspathRowProper = properNodesList.indexOf( crosspathNode ); 098 crosspathNodeIsSeparate = crosspathRowProper == -1; 099 } 100 101 { 102 CountCumulate c; 103 if( count == null ) c = null; 104 else 105 { 106 c = new CountCumulate(); 107 if( candidateNode == null ) // base candidate tier 108 { 109 c.outflowVolume = -1; // not calculated 110 if( count.candidateCount() == 0 ) c.holdVolume = CountCumulate.NO_PARTICIPANTS; 111 else if( count.baseCandidateCount() > properNodesList.size() ) 112 { 113 c.holdVolume = count.holdVolume(); 114 if( c.holdVolume > 0 ) 115 { 116 for( CountNodeW node: properNodesList ) c.holdVolume -= node.holdVolume(); 117 if( pathNodeIsSeparate ) c.holdVolume -= pathNode.holdVolume(); 118 if( crosspathNodeIsSeparate ) c.holdVolume -= crosspathNode.holdVolume(); 119 } 120 } 121 else c = null; // no other nodes 122 } 123 else // voter tier 124 { 125 c.outflowVolume = candidateNode.receiveVolume(); 126 c.holdVolume = -1; // not calculated 127 for( CountNodeW node: properNodesList ) 128 { 129 c.outflowVolume -= node.castVolume(); 130 c.outflowVolume -= node.carryVolume(); 131 } 132 if( pathNodeIsSeparate ) 133 { 134 c.outflowVolume -= pathNode.castVolume(); 135 c.outflowVolume -= pathNode.carryVolume(); 136 } 137 if( crosspathNodeIsSeparate ) 138 { 139 c.outflowVolume -= crosspathNode.castVolume(); 140 c.outflowVolume -= crosspathNode.carryVolume(); 141 } 142 if( c.outflowVolume == 0 ) c = null; // no other nodes 143 } 144 } 145 otherNodesCumulate = c; 146 } 147 148 otherNodesRow = otherNodesCumulate == null? -1: r++; 149 pathRow = pathNodeIsSeparate? r++: pathRowProper; 150 crosspathRow = crosspathNodeIsSeparate? r++: crosspathRowProper; 151 if( count == null ) sumRow = -1; // none for orphan tier 152 else 153 { 154 sumRow = r; 155 r += 2; /* 2 to match its double rowspan, which gives room for its borders 156 etc., so it doesn't affect the layout of other tiers */ 157 } 158 footnoteRow = r; 159 } 160 161 162 163 // -------------------------------------------------------------------------------- 164 165 166 /** The crosspath node, or null if there is none. This corresponds to the node on the 167 * user's own vote path where it crosses the tier. The crosspath node may be in or 168 * out of the proper node list, and equal or unequal to the path node. 169 */ 170 final CountNodeW crosspathNode; 171 172 173 174 /** The row of the crosspath node, or -1 if there is no crosspath node. 175 */ 176 final int crosspathRow; 177 178 179 180 final WP_Votespace.FootnoteBuilder footnoteBuilder = new WP_Votespace.FootnoteBuilder(); 181 182 183 184 /** The first footnote row. 185 */ 186 final int footnoteRow; 187 188 189 190 /** Returns the node for the specified row, which is either a proper node 191 * or a path node (or crosspath node) external to the proper list. 192 */ 193 CountNodeW getNode( final int r ) 194 { 195 final CountNodeW node; 196 if( r == crosspathRow ) node = crosspathNode; 197 else if( r == pathRow ) node = pathNode; 198 else node = properNodesList.get( r ); 199 return node; 200 } 201 202 203 204 /** The row of the last node. It is either a proper node, other node, or external 205 * path or crosspath node. 206 */ 207 int lastNodeRow() 208 { 209 final int followingRow = sumRow == -1? footnoteRow: sumRow; 210 return followingRow - 1; 211 } 212 213 214 215 /** The cumulative count of the other nodes, not individually shown in the 216 * view, or null if there are no other nodes. 217 */ 218 final CountCumulate otherNodesCumulate; 219 220 221 222 /** The row of the other-nodes cumulate, or -1 if there are no other nodes. 223 */ 224 final int otherNodesRow; 225 226 227 228 /** The path node, or null if there is none. This path node may be in or out of the 229 * proper node list, and equal or unequal to the crosspath node. 230 */ 231 final CountNodeW pathNode; 232 233 234 235 /** The row of the path node, or -1 if there is no path node. 236 */ 237 final int pathRow; 238 239 240 241 /** The corresponding {@linkplain votorola.s.gwt.stage.vote.MajorV#place() place} in 242 * the vote track, or {@linkplain votorola.a.count.XCastRelation#UNKNOWN UNKNOWN} if 243 * there is none. 244 */ 245 XCastRelation place = XCastRelation.UNKNOWN; 246 247 248 /** @param t the ordinal of this tier, counting from 0 (leftmost). 249 * @param tN the number of tiers. 250 */ 251 void initPlace( final CountNodeW specificPathNode, final int t, final int tN ) 252 { 253 if( tN == 1 ) place = CO_VOTER; // only one tier, they are base co-candidates 254 else if( specificPathNode.isCandidate() ) 255 { 256 if( t == 0 ) place = VOTER; // first tier is voters 257 else if( t == 1 ) place = CO_VOTER; // second is co-voters or base co-candidates 258 } 259 else // no voters left of specific node, no voter board in vote track 260 { 261 if( !specificPathNode.isVoter() ) // non-participant, orphan 262 { 263 if( t == 1 ) place = CO_VOTER; // second tier is co-voters or base co-candidates 264 } 265 else if( t == 0 ) place = CO_VOTER; // first tier is co-voters or base co-candidates 266 } 267 } 268 269 270 271 /** An unmodifiable list of the proper nodes in this tier. 272 */ 273 final List<CountNodeW> properNodesList; 274 275 276 277 /** The row of the first proper node. 278 */ 279 final int properRow; 280 281 282 283 /** The total number of rows. 284 */ 285 int rowCount() { return footnoteRow + 1; } 286 287 288 289 /** The first of the two sum rows, or -1 if there are none. 290 */ 291 final int sumRow; 292 293 294 295 // ==================================================================================== 296 297 298 /** A total of counts across multiple count nodes. 299 */ 300 static final class CountCumulate 301 { 302 303 /** The total of hold counts, or NO_PARTICIPANTS. This is not calculated (is -1) 304 * for non-base candidates. 305 * 306 * @see CountNodeW#holdVolume() 307 */ 308 long holdVolume; 309 310 311 312 /** The negative value of holdVolume that signals a poll without participants. 313 */ 314 static final long NO_PARTICIPANTS = -2L; 315 316 317 /** The total of combined cast and carry volume. This is not calculated (is -1) 318 * for base candidates. 319 * 320 * @see CountNodeW#castVolume() 321 * @see CountNodeW#carryVolume() 322 */ 323 long outflowVolume; 324 325 } 326 327 328}