001package votorola.s.gwt.scene.feed.ss; // Copyright 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 com.google.gson.stream.*;
004import java.io.IOException;
005import java.sql.SQLException;
006import java.util.*;
007import javax.xml.stream.XMLStreamException;
008import votorola.a.count.*;
009import votorola.a.trust.*;
010import votorola.a.voter.*;
011import votorola.g.util.*;
012import votorola.s.gwt.scene.feed.*;
013
014import static votorola.g.util.ArrayListX.EMPTY_LIST;
015
016
017/** A mutable and serializeable person.
018  */
019public final class PersonJig implements Person, SerialJig
020{
021
022
023    /** Constructs a PersonJig.
024      *
025      *     @param _bite the parent bite jig.
026      */
027    public PersonJig( BiteJig _bite ) { bite = _bite; }
028
029
030
031   // ------------------------------------------------------------------------------------
032
033
034    // adding fields?  ensure they are cleared in serialize()
035
036
037
038    /** Fetches this person's count node and caches it for reuse.
039      *
040      *     @see PollJig#count(PollService.VoteServerScope.Run)
041      *     @throws NullPointerException if setUser() was not called
042      */
043    public CountNodeW countNode( final Count count )
044      throws NoSuchItem, SQLException, XMLStreamException
045    {
046        if( !isCountNodeFresh )
047        {
048            if( user == null ) throw new NullPointerException(); // fail fast
049
050            if( countNode == null || !countNode.email().equals( user.email() ))
051            {
052                countNode = count.countTablePV().get( user.email() );
053            }
054            isCountNodeFresh = true;
055        }
056        if( countNode == null ) throw new NoSuchItem( "countNode" );
057
058        return countNode;
059    }
060
061
062        private CountNodeW countNode;
063
064
065        private boolean isCountNodeFresh;
066
067
068
069    /** Fetches this person's trace node and caches it for reuse.
070      *
071      *     @throws NullPointerException if setUser() was not called
072      */
073    public TraceNode traceNode( final NetworkTrace trace ) throws NoSuchItem, SQLException
074    {
075        if( !isTraceNodeFresh )
076        {
077            if( user == null ) throw new NullPointerException(); // fail fast
078
079            if( traceNode == null || !traceNode.registrant().equals( user ))
080            {
081                traceNode = trace.traceNodeTable().get( user );
082            }
083
084            isTraceNodeFresh = true;
085        }
086        if( traceNode == null ) throw new NoSuchItem( "traceNode" );
087
088        return traceNode;
089    }
090
091
092        private TraceNode traceNode;
093
094
095        private boolean isTraceNodeFresh;
096
097
098
099    /** This person's user identifier, if it is known.
100      */
101    public IDPair user() { return user; }
102
103
104        private IDPair user;
105
106
107        private void user( JsonWriter out ) throws IOException
108        {
109            if( user == null ) return;
110
111            out.name( "username" ).value( user.username() );
112            user = null;
113        }
114
115
116        /** Sets the user identifier.
117          *
118          *     @throws IllegalStateException if the user identifier was already set.
119          */
120        public void setUser( IDPair _user )
121        {
122            if( user != null ) throw new IllegalStateException( "user was already set" );
123
124            user = _user;
125        }
126
127
128
129   // - P e r s o n ----------------------------------------------------------------------
130
131
132    public byte dartSector() { return dartSector; }
133
134
135        private byte dartSector = -1;
136
137
138        private void dartSector( final JsonWriter out ) throws IOException
139        {
140            if( dartSector == -1 ) return; // unknown, this is the default in PersonJS
141
142            out.name( "dartSector" ).value( dartSector );
143            dartSector = -1;
144        }
145
146
147        /** Sets the dart sector.
148          *
149          *     @see #countNode(Count)
150          */
151        public void setDartSector( final CountNodeW countNode )
152        {
153            dartSector = countNode.dartSector();
154        }
155
156
157
158    public ResidenceJig residence() { return residence; }
159
160
161        private ResidenceJig residence;
162
163
164        private void residence( final JsonWriter out ) throws IOException
165        {
166            if( residence == null ) return;
167
168            out.name( "residence" );
169            residence.serialize( out );
170            residence = null;
171        }
172
173
174        private ResidenceJig residenceC;
175
176
177        /** Ensures the residence is set to a jig and caches the jig for reuse.
178          *
179          *     @return the jig that was set.
180          */
181        public ResidenceJig setResidence()
182        {
183            if( residenceC == null ) residenceC = new ResidenceJig();
184
185            residence = residenceC;
186            return residenceC;
187        }
188
189
190
191    public String username() { return user.username(); }
192
193
194
195    public List<PersonJig> voteTrace() { return voteTrace; }
196
197
198        private ArrayList<PersonJig> voteTrace = ArrayListX.emptyList(); // i.e. EMPTY_LIST
199
200
201        private boolean areVoteTracesFresh;
202
203
204        private void voteTrace( final JsonWriter out ) throws IOException
205        {
206            if( !areVoteTracesFresh ) return; // unknown, this is the default in PersonJS
207
208            out.name( "voteTrace" );
209            out.beginArray();
210            final int pN = voteTrace.size();
211            for( int p = 1; p < pN; ++p ) voteTrace.get(p).serialize( out ); // ommiting zero, filled in by PersonJS
212            out.endArray();
213            bite.encachePersons( voteTrace, 1 );
214        }
215
216
217        /** Ensures the vote trace is set.
218          *
219          *     @see #countNode(Count)
220          */
221        public void setVoteTrace( final CountNodeW countNode )
222          throws SQLException, XMLStreamException
223        {
224            if( areVoteTracesFresh ) return;
225
226            areVoteTracesFresh = true; // one shot, despite any exception below
227            final CountNodeW[] nodeTrace = countNode.trace();
228
229            if( voteTrace == EMPTY_LIST ) voteTrace = new ArrayList<PersonJig>(
230              /*initial capacity*/Math.max( nodeTrace.length, 7 ));
231            final int nN = nodeTrace.length;
232            assert nodeTrace[0].email().equals( user.email() );
233            voteTrace.add( PersonJig.this ); // origin
234            for( int n = 1; n < nN; ++n )
235            {
236                final PersonJig candidate = bite.uncachePerson();
237                voteTrace.add( candidate );
238                final CountNodeW candidateNode = nodeTrace[n];
239                candidate.setUser( candidateNode.person() );
240                candidate.setDartSector( candidateNode );
241            }
242        }
243
244
245
246    public List<PersonJig> voteTraceProjected() { return voteTraceProjected; }
247
248
249        private ArrayList<PersonJig> voteTraceProjected;
250
251
252        private void voteTraceProjected( final JsonWriter out ) throws IOException
253        {
254            if( voteTraceProjected == null ) return; // that is the default in PersonJS
255
256            out.name( "voteTraceProjected" );
257            out.beginArray();
258            final int pN = voteTraceProjected.size();
259            for( int p = 1; p < pN; ++p ) voteTraceProjected.get(p).serialize( out ); // ommiting zero, filled in by PersonJS
260            out.endArray();
261            bite.encachePersons( voteTraceProjected, 1 );
262            voteTraceProjected = null;
263        }
264
265
266        private ArrayList<PersonJig> voteTraceProjectedC;
267
268
269     // /** Sets both vote traces from a trace pair.
270     //   *
271     //   *     @throws IllegalStateException if it is detected that a trace was already
272     //   *       set.
273     //   */
274     // public void setVoteTraces( final CR_Vote.TracePair pair )
275     // {
276     //     setVoteTrace( pair.traceAtLastCount );
277     //     final CountNodeW[] newTrace = pair.traceProjected;
278     //     if( newTrace == null ) return;
279     //
280     //     if( voteTraceProjectedC == null ) voteTraceProjectedC = new ArrayList<PersonJig>(
281     //       /*initial capacity*/Math.max( newTrace.length, 7 ));
282     //     voteTraceProjected = voteTraceProjectedC;
283     //     final int nN = newTrace.length;
284     //     assert newTrace[0].voter().username().equals( username );
285     //     voteTrace.add( PersonJig.this ); // origin
286     //     for( int n = 1; n < nN; ++n )
287     //     {
288     //         final CountNodeW node = newTrace[n];
289     //         final PersonJig jig = bite.uncachePerson();
290     //         voteTraceProjected.add( jig );
291     //         jig.setUser( node.voter() );
292     //     }
293     // }
294     ///// update to take CountNodeW per setVoteTrace, so no cost if areVoteTracesFresh already
295
296
297
298   // - S e r i a l - J i g --------------------------------------------------------------
299
300
301    public void serialize( final JsonWriter out ) throws IOException
302    {
303        out.beginObject();
304        dartSector( out );
305        residence( out );
306        user( out );
307        voteTrace( out );
308        voteTraceProjected( out );
309        out.endObject();
310
311        isCountNodeFresh = false;
312        isTraceNodeFresh = false;
313        areVoteTracesFresh = false;
314    }
315
316
317
318//// P r i v a t e ///////////////////////////////////////////////////////////////////////
319
320
321    private final BiteJig bite;
322
323
324}