Source code: com/RuntimeCollective/questionnaire/bean/ClusterSafeQuestionnaire.java
1 /* $Header: /home/CVS/rjp/src/com/RuntimeCollective/questionnaire/bean/ClusterSafeQuestionnaire.java,v 1.5 2003/10/13 11:29:27 miles Exp $
2 * $Revision: 1.5 $
3 * $Date: 2003/10/13 11:29:27 $
4 *
5 * ====================================================================
6 *
7 * Josephine : http://www.runtime-collective.com/josephine/index.html
8 *
9 * Copyright (C) 2003 Runtime Collective
10 *
11 * This product includes software developed by the
12 * Apache Software Foundation (http://www.apache.org/).
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Lesser General Public
16 * License as published by the Free Software Foundation; either
17 * version 2.1 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Lesser General Public License for more details.
23 *
24 * You should have received a copy of the GNU Lesser General Public
25 * License along with this library; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 *
28 */
29
30 package com.RuntimeCollective.questionnaire.bean;
31
32 import com.RuntimeCollective.questionnaire.QuestionnaireException;
33 import com.RuntimeCollective.questionnaire.bean.Question;
34 import com.RuntimeCollective.questionnaire.bean.QuestionnaireLink;
35 import com.RuntimeCollective.questionnaire.bean.UserAnswers;
36 import com.RuntimeCollective.sitemap.bean.Publishable;
37 import com.RuntimeCollective.sitemap.bean.SimplePublishable;
38 import com.RuntimeCollective.webapps.RuntimeParameters;
39 import com.RuntimeCollective.webapps.RuntimeDataSource;
40 import com.RuntimeCollective.webapps.bean.User;
41
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.sql.SQLException;
47
48 /**
49 * An extension of Questionnaire that is (I really hope) cluster-safe
50 * (i.e. works with the SynchronizedEBS).
51 *
52 * @version $Id: ClusterSafeQuestionnaire.java,v 1.5 2003/10/13 11:29:27 miles Exp $
53 */
54 public class ClusterSafeQuestionnaire extends Questionnaire {
55
56
57 /** A list of UserAnswers added to this Questionnaire since the last save. */
58 private List unsaved;
59
60 /** Save this bean to the database. */
61 public void save() {
62
63 // Save the actual questionnaire
64 super.save();
65
66 // Check all unsaved
67 try {
68 ArrayList updates = new ArrayList();
69 String[] updates_array;
70
71 int i=0;
72 while (i<unsaved.size()) {
73 Integer[] oneUnsaved = (Integer[]) unsaved.get(i);
74
75 // Is there now a saved UserAnswers with this id? (belonging to no-one, possibly)
76 int[] ids = RuntimeDataSource.queryInts("select 1 from quest_useranswers where id = "+oneUnsaved[0]);
77
78 // If so, save the mapping, and remove it from unsaved
79 if (ids != null && ids.length > 0) {
80 updates.add((new StringBuffer(105)).append("update quest_useranswers set answered_questionnaire_id = "+id).append(", position_no = "+i).append(" where id = "+oneUnsaved[0]).toString());
81 RuntimeParameters.logDebug(this, "Save: don't need "+oneUnsaved[0]);
82 unsaved.remove(i);
83 } else {
84 i++;
85 }
86 }
87
88 // Do the updates
89 if (!updates.isEmpty()) {
90 updates_array = new String[updates.size()];
91 for (int j=0; j<updates.size(); j++)
92 updates_array[j] = (String) updates.get(j);
93 RuntimeDataSource.update(updates_array);
94 }
95
96
97 } catch (SQLException e) {
98 throw new QuestionnaireException("ClusterSafeQuestionnaire could not save() : "+e);
99 }
100
101 }
102
103
104 /** Add a UserAnswers to this Questionnaire
105 * @param userAnswers, the UserAnswers to add
106 */
107 public void addUserAnswers(UserAnswers userAnswers) {
108 if (userAnswers != null) {
109 Integer provisionalId = getProvisionalPosition();
110 unsaved.add( new Integer[]{ idFromUA(userAnswers),
111 provisionalId });
112 }
113 }
114
115 /** Remove a UserAnswers from this Questionnaire
116 * @param userAnswers, the UserAnswers to remove
117 */
118 public void removeUserAnswers(UserAnswers userAnswers) {
119 if (userAnswers == null)
120 return;
121
122 // remove from unsaved, if present
123 Integer uaId = idFromUA(userAnswers);
124
125 int i=0;
126 while (i<unsaved.size()) {
127 Integer[] oneUnsaved = (Integer[]) unsaved.get(i);
128 if (uaId == oneUnsaved[0]) {
129 RuntimeParameters.logDebug(this, "Removing "+oneUnsaved[0]+" from unsaved");
130 unsaved.remove(i);
131 } else {
132 i++;
133 }
134 }
135 }
136
137
138 /** Get a UserAnswers from this Questionnaire
139 * @param position, the index of the UserAnswers to get
140 * @return the UserAnswers at that index, or null if there is none
141 */
142 public UserAnswers getUserAnswers(int position) {
143 int uaId = -1;
144 UserAnswers oneUA = null;
145
146 // look in unsaved first
147 int i=0;
148 while (i<unsaved.size()) {
149 Integer[] oneUnsaved = (Integer[]) unsaved.get(i);
150 if ( position == oneUnsaved[1].intValue()) {
151 uaId = oneUnsaved[0].intValue();
152 } else {
153 i++;
154 }
155 }
156
157 // no? try the database
158 if (uaId == -1) {
159 try {
160 int[] ids = RuntimeDataSource.queryInts("select 1 from quest_useranswers where answered_questionnaire_id = "+getId()
161 +" and position = "+position);
162 if (ids != null && ids.length > 0) {
163 uaId = ids[0];
164 }
165 } catch (SQLException e) {
166 e.printStackTrace(System.out);
167 RuntimeParameters.logError(this, "getUserAnswers: SQL error getting "+uaId+": "+e);
168 }
169 }
170
171 // try building the UA, if we have one
172 if (uaId != -1) {
173 try {
174 oneUA = (UserAnswers) RuntimeParameters.getStore().get(UserAnswers.class.getName(), uaId);
175 } catch (RuntimeException e) {
176 e.printStackTrace(System.out);
177 RuntimeParameters.logError(this, "getUserAnswers: error getting "+uaId+": "+e);
178 }
179 }
180
181 return oneUA;
182 }
183
184
185 /** Set a UserAnswers in this Questionnaire.
186 * (This adds it to the "unsaved" list.)
187 * @param position, the index where the UserAnswers will be
188 * @param userAnswers, the UserAnswers to put in that position
189 */
190 public void setUserAnswers(int position, UserAnswers userAnswers) {
191 unsaved.add(new Integer[]{ idFromUA(userAnswers),
192 new Integer(position) });
193 }
194
195
196 /** Get the (first) index of a UserAnswers in this Questionnaire
197 * @param userAnswers, the UserAnswers to look for
198 * @return an int, the position of the UserAnswers, or -1 if not found
199 */
200 public int getIndexOfUserAnswers(UserAnswers userAnswers) {
201 if (userAnswers == null)
202 return -1;
203
204 // check db
205 try {
206 int pos[] = RuntimeDataSource.queryInts("select position_no from quest_useranswers where answered_questionnaire_id = "+getId()
207 +"and id = "+userAnswers.getId());
208 if (pos != null && pos.length>0) {
209 return pos[0];
210 }
211 } catch (SQLException e) {
212 e.printStackTrace(System.out);
213 RuntimeParameters.logError(this, "getIndexOfUserAnswers: SQL error checking db: "+e);
214 }
215
216
217 // not there? check unsaved
218 Integer uaId = idFromUA(userAnswers);
219
220 for (int i=0; i<unsaved.size(); i++) {
221 Integer[] oneUnsaved = (Integer[]) unsaved.get(i);
222 if (uaId == oneUnsaved[0]) {
223 RuntimeParameters.logDebug(this, "Found "+uaId+" in unsaved!");
224
225 // Check provisional position, and return
226 return updateProvisionalPosition(i);
227 }
228 }
229
230 return -1;
231 }
232
233
234 /** Get all UserAnswers from this Questionnaire
235 * @return an (ordered) Iterator of UserAnswers
236 */
237 public Iterator getAllUserAnswers() {
238 return getAllUserAnswersAsList().iterator();
239 }
240
241 /**
242 * List version of getAllUserAnswers().
243 * <p>
244 * Adding to / removing from this list will have no effect
245 * on the underlying Questionnaire. It is a throwaway list.
246 */
247 protected List getAllUserAnswersAsList() {
248 ArrayList allUAs = new ArrayList();
249
250 // add all in the db
251 try {
252 int[] rows = RuntimeDataSource.queryInts("select id from "+UserAnswers.DATABASE_TABLE+" where answered_questionnaire_id = "+id+" order by position_no");
253 for (int i=0; i<rows.length; i++) {
254 RuntimeParameters.logDebug(this, "getAllUserAnswers(): from db: "+rows[i]);
255 allUAs.add(uaFromId(new Integer(rows[i])));
256 }
257 } catch (SQLException e) {
258 e.printStackTrace(System.out);
259 RuntimeParameters.logError(this, "getAllUserAnswersAsList: SQL error checking db: "+e);
260 }
261 // add all unsaved
262 for (int i=0; i<unsaved.size(); i++) {
263 Integer[] oneUnsaved = (Integer[]) unsaved.get(i);
264 RuntimeParameters.logDebug(this, "getAllUserAnswers(): from unsaved: "+oneUnsaved[0]);
265 allUAs.add(uaFromId(oneUnsaved[0]));
266 }
267
268 return allUAs;
269 }
270
271 /** Get all member UserAnswers OR anon UserAnswers
272 * @param boolean, membersOnly
273 * @return an (ordered) iterator of either anon or member UserAnswers
274 */
275 public Iterator getAllUserAnswers(boolean membersOnly) {
276 ArrayList ofUserAnswers = new ArrayList();
277 UserAnswers temp;
278
279 Iterator allUAs = getAllUserAnswers();
280
281 if (!membersOnly) {
282 while (allUAs.hasNext()) {
283 temp = (UserAnswers) allUAs.next();
284 if (temp.getAnsweringUser() == null) {
285 ofUserAnswers.add(temp);
286 }
287 }
288 } else {
289 while (allUAs.hasNext()) {
290 temp = (UserAnswers) allUAs.next();
291 if (temp.getAnsweringUser() != null) {
292 ofUserAnswers.add(temp);
293 }
294 }
295 }
296 return ofUserAnswers.iterator();
297 }
298
299
300 /** Get all UserAnswers from a given User for this Questionnaire
301 * @param user, the user whose UserAnswers we want
302 * @return an (ordered) Iterator of UserAnswers
303 */
304 public Iterator getAllUserAnswersForUser(User user) {
305 ArrayList result = new ArrayList();
306
307 if (user != null) {
308 UserAnswers temp;
309
310 Iterator allUAs = getAllUserAnswers();
311 while (allUAs.hasNext()) {
312 temp = (UserAnswers) allUAs.next();
313 if ((temp.getAnsweringUser() != null) &&
314 (temp.getAnsweringUser().getId() == user.getId()))
315 result.add(temp);
316 }
317 }
318
319 return result.iterator();
320 }
321
322
323 /** Get the number of votes, ie the number of UserAnswers
324 * @return an int
325 */
326 public int getNumberOfVotes() {
327 return getAllUserAnswersAsList().size();
328 }
329
330
331 /** Get the number of anonomous votes, ie the number of UsersAnswers where the userId is -1
332 * @return an int
333 */
334 public int getNumberOfAnonVotes() {
335 int result = 0;
336 UserAnswers temp;
337
338 Iterator allUAs = getAllUserAnswers();
339 while (allUAs.hasNext()) {
340 temp = (UserAnswers) allUAs.next();
341 if (temp.getAnsweringUser() == null) {
342 // this was an anon answer - the userId was -1
343 result++;
344 }
345 }
346 return result;
347 }
348
349
350 /**
351 * Initialise this object.
352 */
353 protected void init() {
354 unsaved = Collections.synchronizedList(new ArrayList());
355 // Clear the TheUserAnswers arrayList; it's not used
356 TheUserAnswers = new ArrayList();
357 }
358
359 /*
360 * See superclass.
361 */
362 public ClusterSafeQuestionnaire() {
363 super();
364 init();
365 }
366
367 /** Get a current Questionnaire from the RuntimeDataSource, given an id.
368 * @param id ID of the Questionnaire.
369 */
370 public ClusterSafeQuestionnaire(int id) {
371 super(id);
372 init();
373 }
374
375
376 /**
377 * Gets the next possible position for a UserAnswer,
378 * by examining the database
379 */
380 protected Integer getProvisionalPosition() {
381 int provId = 0;
382
383 try {
384 int[] ids = RuntimeDataSource.queryInts("select max(position_no) from quest_useranswers where answered_questionnaire_id = "+getId());
385 if (ids != null && ids.length > 0) {
386 provId = ids[0] + 1;
387 }
388 } catch (SQLException e) {
389 e.printStackTrace(System.out);
390 RuntimeParameters.logError(this, "getProvisionalPosition: SQL error checking db: "+e);
391 }
392
393 RuntimeParameters.logDebug(this, "ProvisionalId: "+provId);
394
395 return new Integer(provId);
396 }
397
398
399 /**
400 * Check the provisional position for the UserAnswer stored
401 * at position <code>unsavedIndex</code> in the <code>unsaved</code>
402 * List, to make sure it hasn't been used by another UserAnswer
403 * in the meantime. If it has, refresh it, and return it.
404 */
405 protected int updateProvisionalPosition(int unsavedIndex) {
406 Integer[] oneUnsaved = (Integer[]) unsaved.get(unsavedIndex);
407 Integer provPos = oneUnsaved[1];
408 try {
409 int[] ids = RuntimeDataSource.queryInts("select id from quest_useranswers where answered_questionnaire_id = "+getId()
410 +" and position_no = "+provPos);
411 if (ids != null && ids.length > 0) {
412 RuntimeParameters.logDebug(this, "Prov position has been FILLED by "+ids[0]);
413 // get a new provisional id!
414 provPos = getProvisionalPosition();
415 oneUnsaved[1] = provPos;
416 //unsaved.remove(unsavedIndex);
417 //unsaved.add(unsavedIndex, oneUnsaved);
418 }
419 } catch (SQLException e) {
420 e.printStackTrace(System.out);
421 RuntimeParameters.logError(this, "updateProvisionalPosition: SQL error checking db: "+e);
422 }
423
424 RuntimeParameters.logDebug(this, "updateProvPos: return "+provPos);
425
426 return provPos.intValue();
427 }
428
429 /**
430 * Returns the ID of a UserAnswers object, if indeed this object is a UserAnswers
431 * @exception ClassCastException if <code>o</code> is not a UserAnswers
432 */
433 protected Integer idFromUA(Object o) throws ClassCastException {
434 return new Integer( ((UserAnswers) o).getId());
435 }
436
437 /**
438 * Returns a UserAnswers from an Integer object , if indeed this object is an Integer
439 * @exception ClassCastException if <code>o</code> is not a Integer
440 */
441 protected UserAnswers uaFromId(Object o) throws ClassCastException {
442 return (UserAnswers)RuntimeParameters.getStore().get(UserAnswers.class.getName(), ((Integer)o).intValue());
443 }
444
445 }