001package votorola.a.trust; // Copyright 2010-2011, 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.io.*; 004import java.util.*; 005import javax.mail.internet.*; 006import javax.xml.stream.*; 007import votorola.a.*; 008import votorola.a.voter.*; 009import votorola.g.*; 010import votorola.g.lang.*; 011import votorola.g.util.*; 012import votorola.g.xml.stream.*; 013 014 015/** A snapshot of a registrant's input to the voter registry, as transferred from the 016 * streetwiki. It is a precursor to a trace node, which it partially implements. 017 */ 018 @ThreadRestricted( "single writer, readers touch" ) 019public final class Registration implements TraceNode 020{ 021 022 private static final long serialVersionUID = 1L; 023 024 025 026 /** Creates a Registration with all variable items at default values. 027 */ 028 public Registration( IDPair _registrant ) 029 { 030 if( _registrant == null ) throw new NullPointerException(); // fail fast 031 032 registrant = _registrant; 033 } 034 035 036 /** Constructs a Registration from its XML serialization. 037 * 038 * @param r the reader having just read the 'in' start element. 039 * 040 * @see #toXML(votorola.a.VoterInputTable.XMLColumnAppender) 041 */ 042 Registration( final XMLStreamReader r ) throws AddressException, XMLStreamException 043 { 044 final String username = r.getAttributeValue( /*namespaceURI*/null, "username" ); 045 if( username == null ) throw new VotorolaRuntimeException( "missing 'username' attribute: " + LocationX.toString( r.getLocation() )); 046 047 registrant = IDPair.fromUsername( username ); 048 area = r.getAttributeValue( /*namespaceURI*/null, "area" ); 049 countryCode = r.getAttributeValue( /*namespaceURI*/null, "countryCode" ); 050 geohandle = r.getAttributeValue( /*namespaceURI*/null, "geohandle" ); 051 otherProperties = r.getAttributeValue( /*namespaceURI*/null, "other" ); 052 while( r.hasNext() ) 053 { 054 r.next(); 055 if( r.isStartElement() ) 056 { 057 final String elementName = r.getLocalName(); 058 if( "div".equals( elementName )) 059 { 060 addDivision( r.getAttributeValue( /*namespaceURI*/null, "page" )); 061 } 062 else if( "tru".equals( elementName )) 063 { 064 addTrusterByUsername( r.getAttributeValue( /*namespaceURI*/null, "username" )); 065 } 066 } 067 else if( r.isEndElement() && "in".equals( r.getLocalName() )) break; 068 } 069 } 070 071 072 073 // ------------------------------------------------------------------------------------ 074 075 076 /** The set of divisions of which the registrant is a member. The divisions that are 077 * defined in the local wiki are specified by page name, while others are specicified 078 * by remote URL. 079 * 080 * @return unmodifiable set of zero or more wiki pagenames and/or URLs 081 * 082 * @see #addDivision(String) 083 * @see <a href='http://reluk.ca/w/Property:Division' 084 * >zelea.com/w/Property:Division</a> 085 */ 086 public Set<String> divisions() { return divisionsU; } 087 088 089 @ThreadRestricted("constructor") 090 private final HashSet<String> divisions = new HashSet<String>(); 091 092 093 private final Set<String> divisionsU = Collections.unmodifiableSet( divisions ); 094 095 096 /** Adds a division. 097 * 098 * @see #divisions() 099 */ 100 @ThreadRestricted("constructor") 101 public void addDivision( final String div ) { divisions.add( div ); } 102 103 104 105 /** Serializes the common attributes of a registration in XML format. These are the 106 * attributes that are identical for serializing both snapshot input and mounted 107 * output. 108 * 109 * @throws BadInputException 110 */ 111 static @ThreadSafe void toCommonXMLAttributes( final TraceNode node, 112 final VoterInputTable.XMLColumnAppender aC ) throws IOException 113 { 114 aC.appendAttribute( "username", node.registrant().username() ); 115 aC.appendAttribute( "countryCode", node.getCountryCode() ); 116 aC.appendAttribute( "geohandle", node.getGeohandle() ); 117 } 118 119 120 121 /** Serializes the common elements of a registration in XML format. These are the 122 * attributes that are identical for serializing both snapshot input and mounted 123 * output. 124 * 125 * @throws BadInputException 126 */ 127 static @ThreadSafe void toCommonXMLElements( final Set<String> divisions, 128 final VoterInputTable.XMLColumnAppender aC ) throws IOException 129 { 130 if( divisions.size() > 0 ) 131 { 132 final Appendable a = aC.sink(); 133 a.append( "\t\t<divisions>\n" ); 134 for( final String div: divisions ) 135 { 136 a.append( "\t\t\t<div" ); 137 aC.appendAttribute( "page", div ); 138 a.append( "/>\n" ); 139 } 140 a.append( "\t\t\t</divisions>\n" ); 141 } 142 } 143 144 145 146 /** Serializes this registration in XML format. 147 * 148 * @throws BadInputException 149 */ 150 public void toXML( final VoterInputTable.XMLColumnAppender aC ) throws IOException 151 { 152 final Appendable a = aC.sink(); 153 a.append( "\t<in" ); 154 155 // Attributes 156 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 157 toCommonXMLAttributes( Registration.this, aC ); 158 if( area != null ) aC.appendAttribute( "area", area ); 159 160 if( otherProperties != null ) // serialize as an attribute, making it easier to de-serialize 161 { 162 a.append( "\n\t" ); 163 aC.appendAttribute( "other", otherProperties ); 164 } 165 166 a.append( ">\n" ); 167 168 // Common elements 169 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 170 toCommonXMLElements( divisions, aC ); 171 172 // Truster elements 173 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 174 if( trusters.size() > 0 ) 175 { 176 a.append( "\t\t<trusters>\n" ); 177 for( final IDPair truster: trusters ) 178 { 179 a.append( "\t\t\t<tru" ); 180 aC.appendAttribute( "username", truster.username() ); 181 a.append( "/>\n" ); 182 } 183 a.append( "\t\t\t</trusters>\n" ); 184 } 185 186 // - - - 187 a.append( "\t\t</in>\n" ); 188 } 189 190 191 192 /** The list of trust sources for the registrant (destination). When the trust 193 * network is traced, a trust edge will be extended from each source. 194 * 195 * @see TrustEdge 196 */ 197 public List<IDPair> trusters() { return trusters; } 198 199 200 private ArrayListU.Open<IDPair> trusters = new ArrayListU.Open<IDPair>( new IDPair[0] ); // OPT this for speed now, not space 201 202 203 /** Adds a new trust source, if not already present. 204 * 205 * @param newTruster the trust source. 206 * 207 * @see #trusters() 208 */ 209 public void addTruster( final IDPair newTruster ) 210 { 211 if( newTruster == null ) throw new NullPointerException(); // fail fast 212 213 IDPair[] trusterArray = trusters.getBackingArray(); 214 for( IDPair registrant0: trusterArray ) 215 { 216 if( registrant0.equals( newTruster )) return; // already trusting 217 } 218 219 trusterArray = Arrays.copyOf( trusterArray, trusterArray.length + 1 ); 220 trusterArray[trusterArray.length - 1] = newTruster; 221 trusters.setBackingArray( trusterArray ); 222 } 223 224 225 /** Adds a new trust source by username, if not already present. This is a 226 * convenience method. 227 * 228 * @see #addTruster(IDPair) 229 */ 230 public void addTrusterByUsername( final String u ) throws AddressException 231 { 232 addTruster( IDPair.fromUsername( u )); 233 } 234 235 236 /** Removes a trust source, if found. 237 * 238 * @param oldTruster the trust source. 239 * 240 * @see #trusters() 241 */ 242 public void removeTruster( IDPair oldTruster ) 243 { 244 IDPair[] oldArray = trusters.getBackingArray(); 245 int r = 0; 246 for( ;; ++r ) 247 { 248 if( r >= oldArray.length ) return; 249 250 if( oldArray[r].equals( oldTruster )) break; 251 } 252 IDPair[] newArray = new IDPair[oldArray.length - 1]; 253 for( int o = 0, n = 0; o < oldArray.length; ++o ) 254 { 255 if( o == r ) continue; // the removed one 256 257 newArray[n] = oldArray[o]; 258 ++n; 259 } 260 trusters.setBackingArray( newArray ); 261 } 262 263 264 265 // - T r a c e - N o d e -------------------------------------------------------------- 266 267 268 /** @see #setArea(String) 269 */ 270 public final String getArea() { return area; } 271 272 273 private String area; 274 275 276 /** @see #getArea() 277 */ 278 public final void setArea( String newArea ) { area = newArea; } 279 280 281 282 /** @see #setCountryCode(String) 283 */ 284 public final String getCountryCode() { return countryCode; } 285 286 287 private String countryCode; 288 289 290 /** @see #getCountryCode() 291 */ 292 public final void setCountryCode( String newCountryCode ) { countryCode = newCountryCode; } 293 294 295 296 /** @see #setGeohandle(String) 297 */ 298 public final String getGeohandle() { return geohandle; } 299 300 301 private String geohandle; 302 303 304 /** @see #getGeohandle() 305 */ 306 public final void setGeohandle( String newGeohandle ) { geohandle = newGeohandle; } 307 308 309 310 /** @see #setOtherProperties(String) 311 */ 312 public final String getOtherProperties() { return otherProperties; } 313 314 315 private String otherProperties; 316 317 318 /** Sets the other registration properties. Owing to how value is serialized, it 319 * must not contain a double quote (") character. 320 * 321 * @see #getOtherProperties() 322 */ 323 public final void setOtherProperties( String newOtherProperties ) 324 { 325 if( newOtherProperties.indexOf('"') >= 0 ) throw new IllegalArgumentException( "argument contains a double quote (\") character" ); 326 327 otherProperties = newOtherProperties; 328 } 329 330 331 332 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException}. 333 */ 334 public final int primaryTrustEdgeCount() { throw new UnsupportedOperationException(); } 335 336 337 338 public IDPair registrant() { return registrant; } 339 340 341 private final IDPair registrant; 342 343 344 345 /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException}. 346 */ 347 public final int trustLevel() { throw new UnsupportedOperationException(); } 348 349 350 351 // - O b j e c t ---------------------------------------------------------------------- 352 353 354 /** Returns the registrant's username. 355 */ 356 public @Override final String toString() { return registrant.username(); } 357 358 359 360 // ==================================================================================== 361 362 363 /** A context for taking a snapshot of a registration. Each snapshot is taken by 364 * passing an instance of this context (rSC) to the snapRegistration(rSC) method of 365 * script ~/votorola/trust/votrace.js. 366 */ 367 public static @ThreadRestricted("constructor") final class SnapshotContext 368 { 369 370 371 public SnapshotContext( VoteServer _voteServer, Registration _registration ) 372 { 373 voteServer = _voteServer; 374 registration = _registration; 375 } 376 377 378 379 // -------------------------------------------------------------------------------- 380 381 382 /** The vote-server. 383 */ 384 public VoteServer voteServer() { return voteServer; } 385 386 387 private final VoteServer voteServer; 388 389 390 391 /** The empty registration, to be filled with a snapshot of the registrant's 392 * input. 393 */ 394 public Registration registration() { return registration; } 395 396 397 private final Registration registration; 398 399 400 401 } 402 403 404 405}