package 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. import java.io.*; import java.util.*; import javax.mail.internet.*; import javax.xml.stream.*; import votorola.a.*; import votorola.a.voter.*; import votorola.g.*; import votorola.g.lang.*; import votorola.g.util.*; import votorola.g.xml.stream.*; /** A snapshot of a registrant's input to the voter registry, as transferred from the * streetwiki. It is a precursor to a trace node, which it partially implements. */ @ThreadRestricted( "single writer, readers touch" ) public final class Registration implements TraceNode { private static final long serialVersionUID = 1L; /** Creates a Registration with all variable items at default values. */ public Registration( IDPair _registrant ) { if( _registrant == null ) throw new NullPointerException(); // fail fast registrant = _registrant; } /** Constructs a Registration from its XML serialization. * * @param r the reader having just read the 'in' start element. * * @see #toXML(votorola.a.VoterInputTable.XMLColumnAppender) */ Registration( final XMLStreamReader r ) throws AddressException, XMLStreamException { final String username = r.getAttributeValue( /*namespaceURI*/null, "username" ); if( username == null ) throw new VotorolaRuntimeException( "missing 'username' attribute: " + LocationX.toString( r.getLocation() )); registrant = IDPair.fromUsername( username ); area = r.getAttributeValue( /*namespaceURI*/null, "area" ); countryCode = r.getAttributeValue( /*namespaceURI*/null, "countryCode" ); geohandle = r.getAttributeValue( /*namespaceURI*/null, "geohandle" ); otherProperties = r.getAttributeValue( /*namespaceURI*/null, "other" ); while( r.hasNext() ) { r.next(); if( r.isStartElement() ) { final String elementName = r.getLocalName(); if( "div".equals( elementName )) { addDivision( r.getAttributeValue( /*namespaceURI*/null, "page" )); } else if( "tru".equals( elementName )) { addTrusterByUsername( r.getAttributeValue( /*namespaceURI*/null, "username" )); } } else if( r.isEndElement() && "in".equals( r.getLocalName() )) break; } } // ------------------------------------------------------------------------------------ /** The set of divisions of which the registrant is a member. The divisions that are * defined in the local wiki are specified by page name, while others are specicified * by remote URL. * * @return unmodifiable set of zero or more wiki pagenames and/or URLs * * @see #addDivision(String) * @see zelea.com/w/Property:Division */ public Set divisions() { return divisionsU; } @ThreadRestricted("constructor") private final HashSet divisions = new HashSet(); private final Set divisionsU = Collections.unmodifiableSet( divisions ); /** Adds a division. * * @see #divisions() */ @ThreadRestricted("constructor") public void addDivision( final String div ) { divisions.add( div ); } /** Serializes the common attributes of a registration in XML format. These are the * attributes that are identical for serializing both snapshot input and mounted * output. * * @throws BadInputException */ static @ThreadSafe void toCommonXMLAttributes( final TraceNode node, final VoterInputTable.XMLColumnAppender aC ) throws IOException { aC.appendAttribute( "username", node.registrant().username() ); aC.appendAttribute( "countryCode", node.getCountryCode() ); aC.appendAttribute( "geohandle", node.getGeohandle() ); } /** Serializes the common elements of a registration in XML format. These are the * attributes that are identical for serializing both snapshot input and mounted * output. * * @throws BadInputException */ static @ThreadSafe void toCommonXMLElements( final Set divisions, final VoterInputTable.XMLColumnAppender aC ) throws IOException { if( divisions.size() > 0 ) { final Appendable a = aC.sink(); a.append( "\t\t\n" ); for( final String div: divisions ) { a.append( "\t\t\t\n" ); } a.append( "\t\t\t\n" ); } } /** Serializes this registration in XML format. * * @throws BadInputException */ public void toXML( final VoterInputTable.XMLColumnAppender aC ) throws IOException { final Appendable a = aC.sink(); a.append( "\t\n" ); // Common elements // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - toCommonXMLElements( divisions, aC ); // Truster elements // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if( trusters.size() > 0 ) { a.append( "\t\t\n" ); for( final IDPair truster: trusters ) { a.append( "\t\t\t\n" ); } a.append( "\t\t\t\n" ); } // - - - a.append( "\t\t\n" ); } /** The list of trust sources for the registrant (destination). When the trust * network is traced, a trust edge will be extended from each source. * * @see TrustEdge */ public List trusters() { return trusters; } private ArrayListU.Open trusters = new ArrayListU.Open( new IDPair[0] ); // OPT this for speed now, not space /** Adds a new trust source, if not already present. * * @param newTruster the trust source. * * @see #trusters() */ public void addTruster( final IDPair newTruster ) { if( newTruster == null ) throw new NullPointerException(); // fail fast IDPair[] trusterArray = trusters.getBackingArray(); for( IDPair registrant0: trusterArray ) { if( registrant0.equals( newTruster )) return; // already trusting } trusterArray = Arrays.copyOf( trusterArray, trusterArray.length + 1 ); trusterArray[trusterArray.length - 1] = newTruster; trusters.setBackingArray( trusterArray ); } /** Adds a new trust source by username, if not already present. This is a * convenience method. * * @see #addTruster(IDPair) */ public void addTrusterByUsername( final String u ) throws AddressException { addTruster( IDPair.fromUsername( u )); } /** Removes a trust source, if found. * * @param oldTruster the trust source. * * @see #trusters() */ public void removeTruster( IDPair oldTruster ) { IDPair[] oldArray = trusters.getBackingArray(); int r = 0; for( ;; ++r ) { if( r >= oldArray.length ) return; if( oldArray[r].equals( oldTruster )) break; } IDPair[] newArray = new IDPair[oldArray.length - 1]; for( int o = 0, n = 0; o < oldArray.length; ++o ) { if( o == r ) continue; // the removed one newArray[n] = oldArray[o]; ++n; } trusters.setBackingArray( newArray ); } // - T r a c e - N o d e -------------------------------------------------------------- /** @see #setArea(String) */ public final String getArea() { return area; } private String area; /** @see #getArea() */ public final void setArea( String newArea ) { area = newArea; } /** @see #setCountryCode(String) */ public final String getCountryCode() { return countryCode; } private String countryCode; /** @see #getCountryCode() */ public final void setCountryCode( String newCountryCode ) { countryCode = newCountryCode; } /** @see #setGeohandle(String) */ public final String getGeohandle() { return geohandle; } private String geohandle; /** @see #getGeohandle() */ public final void setGeohandle( String newGeohandle ) { geohandle = newGeohandle; } /** @see #setOtherProperties(String) */ public final String getOtherProperties() { return otherProperties; } private String otherProperties; /** Sets the other registration properties. Owing to how value is serialized, it * must not contain a double quote (") character. * * @see #getOtherProperties() */ public final void setOtherProperties( String newOtherProperties ) { if( newOtherProperties.indexOf('"') >= 0 ) throw new IllegalArgumentException( "argument contains a double quote (\") character" ); otherProperties = newOtherProperties; } /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException}. */ public final int primaryTrustEdgeCount() { throw new UnsupportedOperationException(); } public IDPair registrant() { return registrant; } private final IDPair registrant; /** Throws {@linkplain UnsupportedOperationException UnsupportedOperationException}. */ public final int trustLevel() { throw new UnsupportedOperationException(); } // - O b j e c t ---------------------------------------------------------------------- /** Returns the registrant's username. */ public @Override final String toString() { return registrant.username(); } // ==================================================================================== /** A context for taking a snapshot of a registration. Each snapshot is taken by * passing an instance of this context (rSC) to the snapRegistration(rSC) method of * script ~/votorola/trust/votrace.js. */ public static @ThreadRestricted("constructor") final class SnapshotContext { public SnapshotContext( VoteServer _voteServer, Registration _registration ) { voteServer = _voteServer; registration = _registration; } // -------------------------------------------------------------------------------- /** The vote-server. */ public VoteServer voteServer() { return voteServer; } private final VoteServer voteServer; /** The empty registration, to be filled with a snapshot of the registrant's * input. */ public Registration registration() { return registration; } private final Registration registration; } }