001package votorola.s.gwt.mediawiki; // Copyright 2012, 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 com.google.gwt.core.client.*; 004import votorola.a.count.gwt.*; 005import votorola.a.diff.*; 006import votorola.g.lang.*; 007import votorola.s.gwt.stage.*; 008import votorola.s.gwt.stage.vote.*; 009 010import static votorola.a.count.CountNode.DART_SECTOR_MAX; 011import static votorola.s.gwt.stage.vote.LightableDifference.REL_TIGHT_CYCLE; 012 013 014/** The set of all {@linkplain ShadowedDiff shadowed differences} for an {@linkplain 015 * #anchor() anchoring author}. Acknowledgement: The need for something like difference 016 * shadows was first pointed out by Ed Pastore in the Metagovernent mailing list. See <a 017 * href='http://metagovernment.org/pipermail/start_metagovernment.org/2011-February/003588.html' 018 * target='_top'>this post</a>. 019 * 020 * @see DifferenceShadowsV 021 */ 022final class DifferenceShadows 023{ 024 025 026 /** Constructs an instance of DifferenceShadows that co-exists permanently with the 027 * stage. 028 * 029 * @param _voterDiffBoard a sparse array of length {@value 030 * votorola.a.count.CountNode#DART_SECTOR_MAX} containing the differences 031 * between the anchor and her voters. 032 * @param _peerDiffBoard a sparse array of length {@value 033 * votorola.a.count.CountNode#DART_SECTOR_MAX} containing the differences 034 * between the anchor and her peers. 035 * 036 * @see #count() 037 * @see #anchor() 038 * @see #peers() 039 * @see #candidate() 040 * @see #candidateDiff() 041 */ 042 DifferenceShadows( CountJS _count, CountNodeJS _anchor, ShadowedDiff[] _voterDiffBoard, 043 JsArray<CountNodeJS> _peers, ShadowedDiff[] _peerDiffBoard, CountNodeJS _candidate, 044 ShadowedDiff _candidateDiff, final TheatrePage referrer ) 045 { 046 count = _count; assert count != null; 047 anchor = _anchor; assert anchor != null; 048 voterDiffBoard = _voterDiffBoard; assert voterDiffBoard != null; 049 peers = _peers; assert peers != null; 050 peerDiffBoard = _peerDiffBoard; assert peerDiffBoard != null; 051 candidate = _candidate; 052 candidateDiff = _candidateDiff; 053 setReferrer( referrer ); 054 } 055 056 057 058 // ------------------------------------------------------------------------------------ 059 060 061 /** The count node of the anchoring author complete with {@linkplain 062 * CountNodeJS#voters() voters}. 063 */ 064 CountNodeJS anchor() { return anchor; } 065 066 067 private final CountNodeJS anchor; 068 069 070 071 /** The count node of the anchor's candidate complete with her {@linkplain 072 * CountNodeJS#voters() voters}, or null if there is no candidate. 073 */ 074 CountNodeJS candidate() { return candidate; } 075 076 077 private final CountNodeJS candidate; 078 079 080 081 /** Returns the difference between the anchor's position draft and the candidate's; or 082 * null if there is no candidate, or the difference is unknown. 083 */ 084 ShadowedDiff candidateDiff() { return candidateDiff; } 085 086 087 private final ShadowedDiff candidateDiff; 088 089 090 091 /** The overall count for the anchor and other count nodes. 092 */ 093 CountJS count() { return count; } 094 095 096 private final CountJS count; 097 098 099 100 /** Returns the shadowed version of the specified difference, or null if there is 101 * none. 102 */ 103 ShadowedDiff diffFor( final DiffLook d1 ) 104 { 105 if( d1 == null ) return null; 106 107 if( ShadowedDiff.looksLikeShadowedDiff( d1 )) return (ShadowedDiff)d1; // all here in shadows 108 109 ShadowedDiff d2 = candidateDiff; 110 if( d2 != null && d2.key().equals(d1.key()) ) return d2; 111 112 for( int n = 0; n < DART_SECTOR_MAX; ++n ) 113 { 114 d2 = voterDiff( n ); 115 if( d2 != null && d2.key().equals(d1.key()) ) return d2; 116 117 d2 = peerDiff( n ); 118 if( d2 != null && d2.key().equals(d1.key()) ) return d2; 119 } 120 121 return null; 122 } 123 124 125 126 /** Returns the shadowed difference for the specified person and poll, or null if 127 * there is none. 128 */ 129 ShadowedDiff diffFor( final String personName, final String pollName ) 130 { 131 return count.pollName().equals(pollName)? diffFor(personName): null; 132 } 133 134 135 136 /** A sparse array of the anchor's {@linkplain votorola.a.count.CountNode#dartSector() 137 * dart sectored} peers. These are her {@linkplain 138 * votorola.a.count.XCastRelation#CO_VOTER co-voters} unless she is a <a 139 * href='../../../../../../d/theory.xht#base-candidate' target='_top'>root 140 * candidate</a>, in which case they are her fellow {@linkplain 141 * CountJS#baseCandidates() base candidates}. The array is indexed from zero with 142 * null elements for empty sectors. 143 * 144 * @return an array of length {@value 145 * votorola.a.count.CountNode#DART_SECTOR_MAX}. 146 */ 147 JsArray<CountNodeJS> peers() { return peers; } 148 149 150 private final JsArray<CountNodeJS> peers; 151 152 153 154 /** Returns the difference between the position drafts of the anchor and her peer at 155 * the specified index; or null if there is no such peer, or the difference is 156 * unknown. If there is no candidate and the anchor is a root candidate, then the 157 * peers will be fellow base candidates. 158 * 159 * @param n the {@linkplain votorola.a.count.CountNode#dartSector() dart sector} 160 * of the peer less one, from zero (inclusive) to {@value 161 * votorola.a.count.CountNode#DART_SECTOR_MAX} (non-inclusive). 162 */ 163 ShadowedDiff peerDiff( final int n ) { return peerDiffBoard[n]; } 164 165 166 private final ShadowedDiff[] peerDiffBoard; 167 168 169 170 /** Passes all shadowed differences through the specified runner. 171 */ 172 void run( final LightableDifference.Runner<ShadowedDiff> runner ) 173 { 174 ShadowedDiff diff; 175 for( int n = 0; n < DART_SECTOR_MAX; ++n ) 176 { 177 diff = voterDiff( n ); 178 if( diff != null && !diff.stageRelClass().equals(REL_TIGHT_CYCLE) ) runner.run( diff ); 179 // REL_TIGHT_CYCLE means diff will definitely run later as candidate. 180 // Cannot filter out *then*, because cannot be sure that voter was run, 181 // which only happens when candidate is dart sectored 182 } 183 for( int n = 0; n < DART_SECTOR_MAX; ++n ) 184 { 185 diff = peerDiff( n ); 186 if( diff != null ) runner.run( diff ); 187 } 188 diff = candidateDiff(); 189 if( diff != null ) runner.run( diff ); 190 } 191 192 193 194 /** Stages and lights the HTTP referrer's difference, if it is shadowed. 195 */ 196 @Warning("init call") final void setReferrer( final TheatrePage referrer ) 197 { 198 if( referrer == null ) return; // no referrer state to transfer 199 200 final String anchorName = anchor.name(); 201 final Stage stage = Stage.i(); 202 if( !( anchorName.equals(stage.getActorName()) && stage.getDifference() == null )) return; 203 // changed by user or prop, do not clobber 204 205 final String oldActorName = referrer.getActorName(); 206 ShadowedDiff shadowedDiff = diffFor( oldActorName ); 207 if( shadowedDiff == null ) 208 { 209 final String oldDefaultActorName = referrer.getDefaultActorName(); 210 if( !ObjectX.nullEquals( oldActorName, oldDefaultActorName )) 211 { 212 shadowedDiff = diffFor( oldDefaultActorName ); 213 } 214 } 215 if( shadowedDiff == null ) shadowedDiff = diffFor( referrer.getDifference() ); 216 // last for sake of bridge referrer, so bridge's scene diff not lighted when 217 // another was targeted by NominalDifferenceTargeter, for which only the author 218 // was staged 219 if( shadowedDiff == null ) return; // nothing on referrer matches a shadowed diff 220 221 stage.setDifference( shadowedDiff ); 222 Stage.setActorName( shadowedDiff.node().name() ); 223 } 224 225 226 227 /** Returns the difference between the anchor's position draft and the voter's at the 228 * specified index; or null if there is no such voter, or the difference is unknown. 229 * 230 * @param n the {@linkplain votorola.a.count.CountNode#dartSector() dart sector} 231 * of the voter less one, from zero (inclusive) to {@value 232 * votorola.a.count.CountNode#DART_SECTOR_MAX} (non-inclusive). 233 */ 234 ShadowedDiff voterDiff( final int n ) { return voterDiffBoard[n]; } 235 236 237 private final ShadowedDiff[] voterDiffBoard; 238 239 240 241//// P r i v a t e /////////////////////////////////////////////////////////////////////// 242 243 244 private ShadowedDiff diffFor( final String personName ) 245 { 246 if( personName == null ) return null; 247 248 if( personName.equals( anchor.name() )) return null; // no self diff 249 250 if( candidate != null && personName.equals( candidate.name() )) return candidateDiff; 251 252 CountNodeJS node; 253 for( int n = 0; n < DART_SECTOR_MAX; ++n ) 254 { 255 node = anchor.voters().get( n ); 256 if( node != null && personName.equals( node.name() )) return voterDiff( n ); 257 258 node = peers.get( n ); 259 if( node != null && personName.equals( node.name() )) return peerDiff( n ); 260 } 261 262 return null; 263 } 264 265 266 267}