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}