0001: package com.xoetrope.survey;
0002:
0003: import java.io.IOException;
0004: import java.io.Reader;
0005: import java.io.StringReader;
0006: import java.io.Writer;
0007: import java.sql.ResultSet;
0008: import java.sql.Statement;
0009: import java.util.Date;
0010: import java.util.Enumeration;
0011: import java.util.Vector;
0012: import net.xoetrope.optional.data.sql.ConnectionObject;
0013:
0014: import net.xoetrope.xml.XmlElement;
0015: import net.xoetrope.xml.XmlSource;
0016: import net.xoetrope.xml.nanoxml.NanoXmlElement;
0017: import net.xoetrope.xml.nanoxml.NanoXmlWriter;
0018: import net.xoetrope.xui.XProject;
0019: import net.xoetrope.xui.XProjectManager;
0020: import net.xoetrope.xui.data.XBaseModel;
0021: import net.xoetrope.xui.data.XDataBinding;
0022: import net.xoetrope.xui.data.XModel;
0023:
0024: /**
0025: * An Survey contains the definition of a survey, the questions
0026: * and the responses. The definitions are read from file and passed on to the
0027: * question manager for rotation. The survey definition is largely independant
0028: * of how the surveys are presented or rendered</p>
0029: *
0030: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
0031: * the GNU Public License (GPL), please see license.txt for more details. If
0032: * you make commercial use of this software you must purchase a commercial
0033: * license from Xoetrope.</p>
0034: * <p> $Revision: 1.5 $</p>
0035: */
0036: public class Survey {
0037: private Date startTime;
0038: private String name;
0039: private int id;
0040:
0041: /**
0042: * The owning project / the context for this object
0043: */
0044: protected XProject currentProject;
0045:
0046: protected XResponseSet responseSet;
0047:
0048: protected QuestionGroup currentGroup;
0049: protected Question currentQuestion;
0050:
0051: protected int currentQuestionNumber;
0052: protected int currentOptionNumber;
0053: protected int currentCardNumber;
0054: protected int currentPageNr;
0055:
0056: protected Vector groups;
0057:
0058: protected int questionsPerPage = 5;
0059:
0060: /**
0061: * Creates a new instance of Survey
0062: * @param n the name of the survey
0063: * @param qpp the maximum number of questions
0064: * that will be displayed on a page.
0065: */
0066: public Survey(String n, int qpp) {
0067: currentProject = XProjectManager.getCurrentProject();
0068: groups = new Vector();
0069: name = n;
0070: questionsPerPage = qpp;
0071: responseSet = new XResponseSet(name);
0072: startTime = new Date();
0073: }
0074:
0075: /**
0076: * Creates a new survey
0077: * @param n the name of the survey
0078: */
0079: public Survey(String n) {
0080: this (n, 5);
0081: }
0082:
0083: /**
0084: * Creates a new nameless survey.
0085: */
0086: public Survey() {
0087: this ("");
0088: }
0089:
0090: /**
0091: * Gets the "startDate" of this survey
0092: * @return Date the Date object
0093: */
0094: public Date getStartTime() {
0095: return startTime;
0096: }
0097:
0098: /**
0099: * Gets the maximum number of questions that can be displayed
0100: * on a page
0101: * @return number of questions
0102: */
0103: public int getNumQuestionsPerPage() {
0104: return questionsPerPage;
0105: }
0106:
0107: /**
0108: * Sets the maximum number of questions that can be displayed
0109: * on a page.
0110: * @param qpp the number of questions
0111: */
0112: public void setNumQuestionsPerPage(int qpp) {
0113: questionsPerPage = qpp;
0114: }
0115:
0116: /**
0117: * Gets the survey name
0118: * @return the survey name
0119: */
0120: public String getName() {
0121: return name;
0122: }
0123:
0124: /**
0125: * Sets the name of this survey
0126: * @param sn the survey name
0127: */
0128: public void setName(String sn) {
0129: name = sn;
0130: }
0131:
0132: /**
0133: * Gets the id of this survey
0134: * @return the id
0135: */
0136: public int getId() {
0137: return id;
0138: }
0139:
0140: /**
0141: * Sets the id of this survey
0142: * @param i the new id of this survey
0143: */
0144: public void setId(int i) {
0145: id = i;
0146: }
0147:
0148: /**
0149: * Adds the passed question group to this survey
0150: * @param group the question group to be added
0151: */
0152: public void addGroup(QuestionGroup group) {
0153: groups.add(group);
0154: }
0155:
0156: /**
0157: * Gets the question group contained
0158: * in this survey
0159: * @return Vector containing the question groups
0160: * of this survey
0161: */
0162: public Vector getGroups() {
0163: return groups;
0164: }
0165:
0166: /**
0167: * Gets the current question in the
0168: * iteration through this survey.
0169: * @return the current question
0170: */
0171: public Question getCurrentQuestion() {
0172: return currentQuestion;
0173: }
0174:
0175: /**
0176: * Gets the current question group in
0177: * the iteration through this survey.
0178: * @return the current question group
0179: */
0180: public QuestionGroup getCurrentGroup() {
0181: return currentGroup;
0182: }
0183:
0184: /**
0185: * Sets the maximum number of questions that can be displayed
0186: * on one page.
0187: * @param n the new number of questions
0188: */
0189: public void setNumQuestionsPerCard(int n) {
0190: questionsPerPage = n;
0191: }
0192:
0193: /**
0194: * Restarts this survey.
0195: */
0196: public void resetSurvey() {
0197: currentGroup = (groups.size() > 0 ? (QuestionGroup) groups
0198: .get(0) : null);
0199: currentQuestion = null;
0200: currentQuestionNumber = -1;
0201: currentOptionNumber = -1;
0202: currentCardNumber = -1;
0203: currentPageNr = 1;
0204: }
0205:
0206: /**
0207: * Sets the current survey "state" to the begining of given question group
0208: * @param groupId id of the question group
0209: */
0210: public void shiftToGroup(int groupId) {
0211: if (groupId > 0) {
0212: QuestionGroup questionGroup = null;
0213:
0214: Enumeration enumeration = groups.elements();
0215:
0216: boolean b = false;
0217: while (enumeration.hasMoreElements() && !b) {
0218: questionGroup = (QuestionGroup) enumeration
0219: .nextElement();
0220: b = (questionGroup.getId() == groupId);
0221: }
0222: if (!b)
0223: return;
0224:
0225: currentGroup = questionGroup;
0226: currentQuestion = null;
0227: currentQuestionNumber = -1;
0228: currentCardNumber = 0;
0229: currentPageNr++;
0230: } else {
0231: currentGroup = null;
0232: currentQuestion = null;
0233: currentQuestionNumber = -1;
0234: currentCardNumber = 0;
0235: currentPageNr++;
0236: }
0237: }
0238:
0239: /**
0240: * Indicates whether the iteration through
0241: * this survey has next question group.
0242: * @return true if there is next group,
0243: * false otherwise
0244: */
0245: public boolean hasMoreGroups() {
0246: return (currentGroup != null);
0247: }
0248:
0249: /**
0250: * Creates and adds the new condition to the specified set of conditions
0251: * @param conditions Vector containing conditions to which
0252: * the new condition will be added
0253: * @param question the question of the condition
0254: * @param option the question's option whose answer is specified
0255: * @param answer the answer for the specified option
0256: */
0257: protected void addCondition(Vector conditions, Question question,
0258: Option option, String answer) {
0259: Condition condition = null;
0260: Enumeration enumConditions = conditions.elements();
0261: while (enumConditions.hasMoreElements() && condition == null) {
0262: Condition c = (Condition) enumConditions.nextElement();
0263: if (c.getQuestion().equals(question))
0264: condition = c;
0265: }
0266: if (condition == null) {
0267: condition = new Condition(question);
0268: conditions.add(condition);
0269: }
0270: condition.addAnswer(option, answer);
0271: }
0272:
0273: /**
0274: * Gets the question with the specified id
0275: * @param questionId the id of the question
0276: * @return the question
0277: */
0278: public Question getQuestionById(int questionId) {
0279: Question question = null;
0280: Enumeration enumGroups = groups.elements();
0281: while (enumGroups.hasMoreElements() && (question == null)) {
0282: QuestionGroup group = (QuestionGroup) enumGroups
0283: .nextElement();
0284: question = group.getQuestionById(questionId);
0285: }
0286: return question;
0287: }
0288:
0289: /**
0290: * Saves the user given responses to the data model.
0291: * @param group the group of questions whose respones
0292: * are specified
0293: * @param responses the responses to be saved
0294: * @parm page the number of page on which the responses
0295: * have been given.
0296: */
0297: public void setUserResponses(QuestionGroup group, Vector responses,
0298: int page) {
0299: if (group == null || responses == null)
0300: return;
0301: Vector questions = group.getQuestions();
0302: for (int i = 0; i < questions.size();) {
0303: Question question = (Question) questions.get(i);
0304: page += (++i % (questionsPerPage + 1) == 0 ? 1 : 0);
0305: String modelPath = "xui_state/currentSurvey/" + page + "/"
0306: + group.getId();
0307: Condition response = getResponse(responses, question);
0308: if (response != null)
0309: setResponse(response, modelPath);
0310: }
0311: }
0312:
0313: /**
0314: * Gets the user's response for the specified question. The Condition
0315: * instance is returned as it is suitable for storing user given response
0316: * @param respones Vector containing responses from which
0317: * the wanted response is to be retrieved.
0318: * @param question the question that the response for which is
0319: * to be retreived
0320: * @return the response given by the user
0321: */
0322: protected Condition getResponse(Vector responses, Question question) {
0323: Condition response = null;
0324: Enumeration enumResponses = responses.elements();
0325: while (enumResponses.hasMoreElements() && response == null) {
0326: Condition r = (Condition) enumResponses.nextElement();
0327: if (r.getQuestion().equals(question))
0328: response = r;
0329: }
0330: return response;
0331: }
0332:
0333: /**
0334: * Stores the specified response in the data model.
0335: * @param response user given response
0336: * @param modelPath the path to the model node where the
0337: * response will be stored.
0338: */
0339: protected void setResponse(Condition response, String modelPath) {
0340: Question question = response.getQuestion();
0341: String questionModelPath = modelPath + "/" + question.getId();
0342: XModel questionModel = (XModel) currentProject.getModel().get(
0343: questionModelPath);
0344: int questionType = question.getQuestionType();
0345:
0346: if (questionType == Question.MULTIPLE_CHOICE) {
0347: int numOptions = question.getNumOptions();
0348: for (int i = 0; i < numOptions; i++) {
0349: Option option = (Option) question.getOptions().get(i);
0350: XBaseModel optionModel = new XBaseModel();
0351: optionModel.setId(String.valueOf(option.getId()));
0352: String optionValue = "0";
0353: Enumeration enumOptions = response.getOptions()
0354: .elements();
0355: while (enumOptions.hasMoreElements()
0356: && optionValue.equals("0")) {
0357: Option o = (Option) enumOptions.nextElement();
0358: if (o.equals(option))
0359: optionValue = "1";
0360: }
0361: optionModel.set(optionValue);
0362: questionModel.append(optionModel);
0363: }
0364: } else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
0365: if (response.getOptions().size() > 0) {
0366: Option option = (Option) response.getOptions().get(0);
0367: String optionText = option.getText();
0368: questionModel.setAttribValue(0, optionText);
0369: }
0370: } else if (questionType == Question.FREE_TEXT) {
0371: int numOptions = question.getNumOptions();
0372: for (int i = 0; i < numOptions; i++) {
0373: Option option = (Option) question.getOptions().get(i);
0374: Option responseOption = null;
0375: String answer = null;
0376:
0377: int numResponseOptions = response.getOptions().size();
0378: for (int j = 0; (j < numResponseOptions)
0379: && (responseOption == null); j++) {
0380: Option o = (Option) response.getOptions().get(j);
0381: answer = (String) response.getAnswers().get(j);
0382: if (o.equals(option))
0383: responseOption = o;
0384: }
0385:
0386: if (responseOption != null) {
0387: XBaseModel optionModel = new XBaseModel();
0388: optionModel.setId(String.valueOf(responseOption
0389: .getId()));
0390: optionModel.setAttribValue(0, answer);
0391: questionModel.append(optionModel);
0392: }
0393: }
0394: }
0395: }
0396:
0397: /**
0398: * Gets the responses given by the user
0399: * @return Vector containing responses
0400: */
0401: public Vector getUserResponses() {
0402: Vector responses = new Vector();
0403:
0404: for (int page = 1; page <= currentPageNr; page++) {
0405: String modelPath = "xui_state/currentSurvey/" + page;
0406: XModel pageModel = (XModel) currentProject.getModel().get(
0407: modelPath);
0408: int numGroups = pageModel.getNumChildren();
0409: for (int i = 0; i < numGroups; i++) {
0410: XModel groupModel = pageModel.get(i);
0411:
0412: int numQuestions = groupModel.getNumChildren();
0413: for (int j = 0; j < numQuestions; j++) {
0414: XModel questionModel = groupModel.get(j);
0415: int questionId = new Integer(questionModel.getId())
0416: .intValue();
0417: Question question = getQuestionById(questionId);
0418: int questionType = question.getQuestionType();
0419: if (questionType == Question.MULTIPLE_CHOICE) {
0420: int numOptions = questionModel.getNumChildren();
0421: for (int k = 0; k < numOptions; k++) {
0422: XModel optionModel = questionModel.get(k);
0423: Object o = optionModel.get();
0424: if ((optionModel == null) || (o == null)
0425: || (o.toString().equals("0")))
0426: continue;
0427: int optionId = new Integer(optionModel
0428: .getId()).intValue();
0429: Option option = question
0430: .getOptionById(optionId);
0431: String answer = option.getText();
0432: addCondition(responses, question, option,
0433: answer);
0434: }
0435: } else if (questionType == Question.FREE_TEXT) {
0436: int numOptions = questionModel.getNumChildren();
0437: for (int k = 0; k < numOptions; k++) {
0438: XModel optionModel = questionModel.get(k);
0439: String answer = optionModel
0440: .getAttribValueAsString(0);
0441: if (answer == null)
0442: continue;
0443: int optionId = new Integer(optionModel
0444: .getId()).intValue();
0445: Option option = question
0446: .getOptionById(optionId);
0447: addCondition(responses, question, option,
0448: answer);
0449: }
0450: } else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
0451: String answer = questionModel
0452: .getAttribValueAsString(0);
0453: if ((answer == null) || (answer.equals("")))
0454: continue;
0455: int optionId = question
0456: .getOptionIdByText(answer);
0457: Option option = question
0458: .getOptionById(optionId);
0459: addCondition(responses, question, option,
0460: answer);
0461: }
0462: }
0463: }
0464: }
0465: return responses;
0466: }
0467:
0468: /**
0469: * Gets the responses from the current question group.
0470: * @return Vector containig responses
0471: */
0472: public Vector getCurrentGroupResponses() {
0473: int numCards = (int) Math.ceil((double) currentGroup
0474: .getNumQuestions()
0475: / questionsPerPage);
0476: Vector conditions = new Vector();
0477:
0478: while (numCards-- > 0) {
0479: String modelPath = ("currentSurvey/"
0480: + (currentPageNr - numCards) + "/" + currentGroup
0481: .getId());
0482: XModel groupModel = (XModel) currentProject.getModel().get(
0483: modelPath);
0484:
0485: int numQuestions = groupModel.getNumChildren();
0486: for (int i = 0; i < numQuestions; i++) {
0487: XModel questionModel = groupModel.get(i);
0488: int questionId = new Integer(questionModel.getId())
0489: .intValue();
0490:
0491: Question question = currentGroup
0492: .getQuestionById(questionId);
0493: int questionType = question.getQuestionType();
0494:
0495: if (questionType == Question.MULTIPLE_CHOICE) {
0496: int numOptions = questionModel.getNumChildren();
0497: for (int j = 0; j < numOptions; j++) {
0498: XModel optionModel = questionModel.get(j);
0499: if ((optionModel == null)
0500: || (optionModel.getAttribValueAsInt(1) == 0))
0501: continue;
0502: int optionId = optionModel
0503: .getAttribValueAsInt(1);
0504: Option option = question
0505: .getOptionById(optionId);
0506: String answer = option.getText();
0507: addCondition(conditions, question, option,
0508: answer);
0509: }
0510: } else if (questionType == Question.FREE_TEXT) {
0511: int numOptions = questionModel.getNumChildren();
0512: for (int j = 0; j < numOptions; j++) {
0513: XModel optionModel = questionModel.get(j);
0514: String answer = optionModel
0515: .getAttribValueAsString(0);
0516: if ((answer == null) || answer.equals(""))
0517: continue;
0518: int optionId = new Integer(optionModel.getId())
0519: .intValue();
0520: Option option = question
0521: .getOptionById(optionId);
0522: addCondition(conditions, question, option,
0523: answer);
0524: }
0525: } else if (questionType == Question.MUTUALLY_EXCLUSIVE) {
0526: String answer = questionModel
0527: .getAttribValueAsString(0);
0528: if ((answer == null) || answer.equals(""))
0529: continue;
0530: int optionId = question.getOptionIdByText(answer);
0531: Option option = question.getOptionById(optionId);
0532: addCondition(conditions, question, option, answer);
0533: }
0534: }
0535: }
0536: return conditions;
0537:
0538: }
0539:
0540: /**
0541: * Goes through survey history, allows to show previous question page in
0542: * a survey.
0543: * @param pageNr page number
0544: * @return page name
0545: */
0546: public String setPage(int pageNr) {
0547: String path = "currentSurveyHistory/" + pageNr;
0548: XModel historyModel = (XModel) currentProject.getModel().get(
0549: path);
0550: if (historyModel.getNumChildren() == 0)
0551: return null;
0552:
0553: XModel groupIdModel = (XModel) historyModel.get(0);
0554: int groupId = new Integer(groupIdModel.getId()).intValue();
0555: if (groupIdModel.getNumChildren() == 0)
0556: return null;
0557:
0558: XModel cardNrModel = (XModel) groupIdModel.get(0);
0559: int cardNr = new Integer(cardNrModel.getId()).intValue();
0560: if (cardNrModel.getNumChildren() == 0)
0561: return null;
0562:
0563: XModel pageNameModel = (XModel) cardNrModel.get(0);
0564: String pageName = pageNameModel.getId();
0565:
0566: QuestionGroup questionGroup = getGroupById(groupId);
0567: if (questionGroup == null)
0568: return "";
0569:
0570: currentPageNr = pageNr;
0571: currentGroup = questionGroup;
0572: currentQuestion = null;
0573: currentQuestionNumber = -1;
0574: currentCardNumber = cardNr;
0575:
0576: return pageName;
0577: }
0578:
0579: /**
0580: * Sets the previously displayed question page
0581: * as the current one.
0582: * @return the name of the current page
0583: */
0584: public String setPrevPage() {
0585: return setPage(--currentPageNr);
0586: }
0587:
0588: /**
0589: * Sets the first page as the current one
0590: * @return the name of the current page
0591: */
0592: public String setFirstPage() {
0593: return setPage(2);
0594: }
0595:
0596: /**
0597: * moves survey "state" to the next question group
0598: * @return false if the current question group
0599: * is the last one in the current survey.
0600: */
0601: public boolean shiftToNextGroup() {
0602: Vector responses = getUserResponses();
0603: RuleEngine ruleEngine = getRuleEngine();
0604: ruleEngine.handleGroup(currentGroup, responses, currentGroup
0605: .getXmlRules());
0606: return (currentGroup != null);
0607: }
0608:
0609: /**
0610: * Gets the rule engine being used in this survey
0611: * @return current rule engine
0612: */
0613: private RuleEngine getRuleEngine() {
0614: return (RuleEngine) currentProject.getObject("RuleEngine");
0615: }
0616:
0617: /**
0618: * Gets the index of the current question
0619: * @return the index
0620: */
0621: public int getCurrentQuestionIndex() {
0622: return (currentCardNumber * questionsPerPage + currentQuestionNumber);
0623: }
0624:
0625: /**
0626: * Indicates whether the iteration through
0627: * this survey has more question groups
0628: * @true if the iteration has more question groups,
0629: * false otherwise
0630: */
0631: public boolean hasMoreQuestions() {
0632: return (currentGroup != null);
0633: }
0634:
0635: /**
0636: * Indicates whether there are more questions to show on the current page.
0637: * @return true if the current group has more questions, false
0638: * otherwise
0639: */
0640: public boolean hasMoreQuestionsToShow() {
0641: return ((currentGroup != null)
0642: && ((getCurrentQuestionIndex() + 1) < currentGroup
0643: .getNumQuestions()) && (currentQuestionNumber + 1 < questionsPerPage));
0644: }
0645:
0646: /**
0647: * Gets whether current question groups has more cards
0648: * to be displayed.
0649: * @return true if the group has more cards, false
0650: * otherwise
0651: */
0652: public Boolean hasMoreCards() {
0653: return new Boolean(
0654: (currentCardNumber + 1) * questionsPerPage < currentGroup
0655: .getNumQuestions());
0656: }
0657:
0658: /**
0659: * Moves the current survey "state" to the next page of questions
0660: * @return false if the current page questions was the last one
0661: * in the current survey.
0662: */
0663: public boolean shiftToNextPage() {
0664: if (hasMoreCards().booleanValue()) {
0665: shiftToNextCard();
0666: return true;
0667: } else {
0668: return shiftToNextGroup();
0669: }
0670: }
0671:
0672: /**
0673: * Shifts the current survey state to the next card
0674: * of questions
0675: */
0676: public void shiftToNextCard() {
0677: currentQuestionNumber = -1;
0678: currentCardNumber++;
0679: currentPageNr++;
0680: }
0681:
0682: /**
0683: * Deletes survey history for the given page
0684: * @param pageNr the number of page
0685: */
0686: public void deletePageModel(int pageNr) {
0687: XModel model = (XModel) currentProject.getModel().get(
0688: "xui_state/currentSurvey/" + pageNr);
0689: model.removeChildren();
0690: }
0691:
0692: /**
0693: * Checks whether current question in the current survey has more options
0694: * @return false if the current option is the last one.
0695: */
0696: public boolean hasMoreOptions() {
0697: return ((currentQuestion != null) && (currentOptionNumber + 1 < currentQuestion
0698: .getNumOptions()));
0699: }
0700:
0701: /**
0702: * Returns text of the next question in the current survey.
0703: * @return question text
0704: */
0705: public String getNextQuestionText() {
0706: currentOptionNumber = -1;
0707: currentQuestionNumber++;
0708: currentQuestion = currentGroup
0709: .getQuestion(getCurrentQuestionIndex());
0710:
0711: return (currentQuestion != null ? currentQuestion.getText()
0712: : null);
0713: }
0714:
0715: /**
0716: * Gets the next question in the current survey
0717: * @return the next question
0718: */
0719: public Question getNextQuestion() {
0720: currentOptionNumber = -1;
0721: currentQuestionNumber++;
0722: currentQuestion = currentGroup
0723: .getQuestion(getCurrentQuestionIndex());
0724: return currentQuestion;
0725: }
0726:
0727: /**
0728: * Sets the specified question as the current in the
0729: * internal survey state.
0730: * @param questionId the id of the question
0731: * @return the name of the current question
0732: */
0733: public String setCurrentQuestion(int questionId) {
0734: String questionName = "";
0735: Question question = currentGroup.getQuestionById(questionId);
0736: if (question != null) {
0737: currentOptionNumber = -1;
0738: currentQuestionNumber = currentGroup
0739: .getQuestionIdxById(questionId);
0740: currentQuestion = question;
0741: questionName = currentQuestion.getText();
0742: }
0743: return questionName;
0744: }
0745:
0746: /**
0747: * Sets the specified group as the current in the
0748: * internal survey state
0749: * @param groupId the id of the question group
0750: */
0751: public void setCurrentGroup(int groupId) {
0752: if (groupId > 0) {
0753: QuestionGroup group = null, g = null;
0754:
0755: Enumeration enumeration = groups.elements();
0756: while (enumeration.hasMoreElements() && (group == null)) {
0757: g = (QuestionGroup) enumeration.nextElement();
0758: if (g.getId() == groupId)
0759: group = g;
0760: }
0761:
0762: if (group != null) {
0763: currentGroup = group;
0764: currentQuestion = null;
0765: currentQuestionNumber = -1;
0766: currentOptionNumber = -1;
0767: }
0768:
0769: }
0770: }
0771:
0772: /**
0773: * Gets the id of the next question group
0774: * of this survey
0775: * @return the id of the question group
0776: */
0777: public int getNextGroupId() {
0778: int max = 0;
0779: Enumeration enumGroups = groups.elements();
0780: while (enumGroups.hasMoreElements()) {
0781: QuestionGroup group = (QuestionGroup) enumGroups
0782: .nextElement();
0783: if (group.getId() > max)
0784: max = group.getId();
0785: }
0786: return (max + 1);
0787: }
0788:
0789: /**
0790: * Gest the id of the next question of this survey
0791: * @return id the id of the next question
0792: */
0793: public int getNextQuestionId() {
0794: int max = 0;
0795: Enumeration enumGroups = groups.elements();
0796: while (enumGroups.hasMoreElements()) {
0797: QuestionGroup questionGroup = (QuestionGroup) enumGroups
0798: .nextElement();
0799: Enumeration enumQuestions = questionGroup.getQuestions()
0800: .elements();
0801: while (enumQuestions.hasMoreElements()) {
0802: Question question = (Question) enumQuestions
0803: .nextElement();
0804: if (question.getId() > max)
0805: max = question.getId();
0806: }
0807: }
0808: return (max + 1);
0809: }
0810:
0811: /**
0812: * Gets the question group with the specified id
0813: * @param groupId the id of the question group which
0814: * is to be retrieved
0815: * @return question group
0816: */
0817: public QuestionGroup getGroupById(int groupId) {
0818: QuestionGroup questionGroup = null;
0819: Enumeration enumeration = groups.elements();
0820: while (enumeration.hasMoreElements() && questionGroup == null) {
0821: QuestionGroup q = (QuestionGroup) enumeration.nextElement();
0822: if (q.getId() == groupId)
0823: questionGroup = q;
0824: }
0825:
0826: return questionGroup;
0827: }
0828:
0829: /**
0830: * Gest the question group at the specified location
0831: * @param idx index of the wanted question group.
0832: * @reutrn the question group
0833: */
0834: public QuestionGroup getGroup(int idx) {
0835: return (0 <= idx && idx < groups.size() ? (QuestionGroup) groups
0836: .get(idx)
0837: : null);
0838: }
0839:
0840: /**
0841: * Gets the number of question groups that
0842: * this survey contains
0843: * @return the number of groups.
0844: */
0845: public int getNumGroups() {
0846: return groups.size();
0847: }
0848:
0849: /**
0850: * Gest the id of the current question of this survey
0851: * @return the id
0852: */
0853: public String getCurrentQuestionId() {
0854: return (currentQuestion != null ? new Integer(currentQuestion
0855: .getId()).toString() : "0");
0856: }
0857:
0858: /**
0859: * Gets the id of the current group of this survey
0860: * @return the current group id
0861: */
0862: public String getCurrentGroupId() {
0863: return String.valueOf(currentGroup.getId());
0864: }
0865:
0866: /**
0867: * Gest the text of the next option in this survey
0868: * @return text of the next option
0869: */
0870: public String getNextOptionText() {
0871: currentOptionNumber++;
0872: return currentQuestion.getOptionText(currentOptionNumber);
0873: }
0874:
0875: /**
0876: * Gets the id of the next option
0877: * @return the next option's id
0878: */
0879: public String getNextOptionId() {
0880: return String.valueOf(currentQuestion
0881: .getOptionId(++currentOptionNumber));
0882: }
0883:
0884: /**
0885: * Gets the id of the current option
0886: * @return the current option id
0887: */
0888: public String getCurrentOptionId() {
0889: return String.valueOf(currentQuestion
0890: .getOptionId(currentOptionNumber));
0891: }
0892:
0893: /**
0894: * Gets the current option text
0895: * @return text of the current option
0896: */
0897: public String getCurrentOptionText() {
0898: return currentQuestion.getOptionText(currentOptionNumber);
0899: }
0900:
0901: /**
0902: * Gets the number of the current page
0903: * @return the number of the current page
0904: */
0905: public String getCurrentPageNr() {
0906: return String.valueOf(currentPageNr);
0907: }
0908:
0909: /**
0910: * Gets the number of the current page
0911: * @return the number of the current page
0912: */
0913: public int getPageNrAsInt() {
0914: return currentPageNr;
0915: }
0916:
0917: /**
0918: * Gets the text of the current question
0919: * @return current question's text
0920: */
0921: public String getCurrentQuestionText() {
0922: return (currentQuestion != null ? currentQuestion.getText()
0923: : "err");
0924: }
0925:
0926: /**
0927: * Gets the number of the currently displayed card
0928: * @return the current card number
0929: */
0930: public int getCurrentCardNumber() {
0931: return currentCardNumber;
0932: }
0933:
0934: /**
0935: * Saves responses from the current survey, given by the user,
0936: * to the output stream.
0937: */
0938: public void writeUserResponses(Writer w, Vector responses)
0939: throws IOException {
0940: NanoXmlWriter writer = new NanoXmlWriter(w);
0941: if (responses == null)
0942: responses = getUserResponses();
0943:
0944: XmlElement responsesXml = new NanoXmlElement("Responses");
0945: XmlElement surveyXml = new NanoXmlElement("Survey");
0946: surveyXml.setAttribute("name", name);
0947:
0948: Enumeration enumResponses = responses.elements();
0949: while (enumResponses.hasMoreElements()) {
0950: Condition condition = (Condition) enumResponses
0951: .nextElement();
0952: Question question = condition.getQuestion();
0953:
0954: XmlElement questionXml = new NanoXmlElement("Question");
0955: surveyXml.addChild(questionXml);
0956: questionXml.setAttribute("id", String.valueOf(question
0957: .getId()));
0958: questionXml.setAttribute("text", question.getText());
0959:
0960: int numResponses = condition.getOptions().size();
0961: for (int i = 0; i < numResponses; i++) {
0962: Option option = (Option) condition.getOptions().get(i);
0963: String answer = (String) condition.getAnswers().get(i);
0964:
0965: XmlElement responseXml = new NanoXmlElement("Response");
0966: questionXml.addChild(responseXml);
0967: responseXml.setAttribute("option_id", String
0968: .valueOf(option.getId()));
0969: responseXml.setAttribute("option_text", option
0970: .getText());
0971: if (question.getQuestionType() == Question.FREE_TEXT)
0972: responseXml.setAttribute("answer", answer);
0973: }
0974: }
0975: responsesXml.addChild(surveyXml);
0976: writer.write(responsesXml, true, 4);
0977: }
0978:
0979: /**
0980: * Writes the current survey's data model to the output stream.
0981: */
0982: public void write(Writer w) throws IOException {
0983: NanoXmlWriter writer = new NanoXmlWriter(w);
0984:
0985: XmlElement surveyXml = new NanoXmlElement("Survey");
0986: Enumeration enumGroups = groups.elements();
0987: while (enumGroups.hasMoreElements()) {
0988: QuestionGroup group = (QuestionGroup) enumGroups
0989: .nextElement();
0990: String groupId = String.valueOf(group.getId());
0991: String nextGroupId = String.valueOf(group.getNextGroupId());
0992: String groupName = group.getName();
0993:
0994: XmlElement groupXml = new NanoXmlElement("Group");
0995: groupXml.setAttribute("id", groupId);
0996: groupXml.setAttribute("next", nextGroupId);
0997: groupXml.setAttribute("name", groupName);
0998:
0999: Enumeration enumQuestions = group.getQuestions().elements();
1000: while (enumQuestions.hasMoreElements()) {
1001:
1002: Question question = (Question) enumQuestions
1003: .nextElement();
1004: String questionId = String.valueOf(question.getId());
1005: String questionText = question.getText();
1006: String questionType = question.getQuestionTypeText();
1007:
1008: XmlElement questionXml = new NanoXmlElement("Q");
1009: questionXml.setAttribute("id", questionId);
1010: questionXml.setAttribute("A", questionText);
1011: questionXml.setAttribute("type", questionType);
1012:
1013: Enumeration enumOptions = question.getOptions()
1014: .elements();
1015: while (enumOptions.hasMoreElements()) {
1016: Option option = (Option) enumOptions.nextElement();
1017: String optionId = String.valueOf(option.getId());
1018: String optionText = option.getText();
1019:
1020: XmlElement optionXml = new NanoXmlElement("R");
1021: optionXml.setAttribute("id", optionId);
1022: optionXml.setAttribute("A", optionText);
1023:
1024: questionXml.addChild(optionXml);
1025: }
1026: groupXml.addChild(questionXml);
1027: }
1028: groupXml.addChild(group.getXmlRules());
1029: surveyXml.addChild(groupXml);
1030: }
1031: writer.write(surveyXml, true, 4);
1032: }
1033:
1034: /**
1035: * Reads the survey from the input stream.
1036: * @param r the input stream reader.
1037: */
1038: public void read(Reader r) {
1039: XmlElement root = XmlSource.read(r);
1040: Vector rootChildren = root.getChildren();
1041: if (rootChildren == null)
1042: return;
1043:
1044: int scale = 4;
1045: try {
1046: scale = new Integer(root.getAttribute("scale")).intValue();
1047: } catch (NumberFormatException e) {
1048: }
1049:
1050: for (int i = 0; i < rootChildren.size(); i++) {
1051: XmlElement xmlGroup = (XmlElement) root.elementAt(i);
1052: char c = xmlGroup.getName().charAt(0);
1053: if (!xmlGroup.getName().equals("Group"))
1054: continue;
1055:
1056: int groupId = new Integer(xmlGroup.getAttribute("id"))
1057: .intValue();
1058: String nextGroupS = xmlGroup.getAttribute("next");
1059: int nextGroup = (nextGroupS == null
1060: || nextGroupS.equals("") ? 0 : new Integer(
1061: nextGroupS).intValue());
1062: String groupName = xmlGroup.getAttribute("name");
1063: QuestionGroup currentGroup = new QuestionGroup(groupId,
1064: nextGroup, groupName);
1065:
1066: Vector groupChildren = xmlGroup.getChildren();
1067: for (int j = 0; j < groupChildren.size(); j++) {
1068: XmlElement groupElement = (XmlElement) groupChildren
1069: .elementAt(j);
1070: String groupElementName = groupElement.getName();
1071: if (groupElementName.equals("Q")) {
1072: int questionId = new Integer(groupElement
1073: .getAttribute("id")).intValue();
1074:
1075: String questionText = groupElement
1076: .getAttribute("A");
1077:
1078: int questionType = Question.DEFAULT_TYPE;
1079: String typeText = groupElement.getAttribute("type");
1080: if (typeText != null) {
1081: if (typeText.equals("mutually exclusive"))
1082: questionType = Question.MUTUALLY_EXCLUSIVE;
1083: else if (typeText.equals("multiple choice"))
1084: questionType = Question.MULTIPLE_CHOICE;
1085: else if (typeText.equals("free text"))
1086: questionType = Question.FREE_TEXT;
1087: }
1088:
1089: Question currentQuestion = new Question(questionId,
1090: currentGroup, questionType, questionText);
1091:
1092: Vector responses = groupElement.getChildren();
1093: for (int k = 0; k < responses.size(); k++) {
1094: XmlElement xmlResponse = (XmlElement) responses
1095: .elementAt(k);
1096: if (!xmlResponse.getName().equals("R"))
1097: continue;
1098:
1099: int responseId = Integer.parseInt(xmlResponse
1100: .getAttribute("id"));
1101: String responseText = xmlResponse
1102: .getAttribute("A");
1103: currentQuestion.addOption(responseId,
1104: responseText);
1105: }
1106: currentGroup.addQuestion(currentQuestion);
1107:
1108: } else if (groupElementName.equals("Rules"))
1109: currentGroup.setXmlRules(groupElement);
1110:
1111: }
1112: addGroup(currentGroup);
1113: }
1114: restartSurvey();
1115: }
1116:
1117: protected void read(ConnectionObject connObj, String surveyName,
1118: String languageCode) throws Exception {
1119: // retrieve the survey id
1120: int surveyId = -1;
1121: Statement surveyIdStmt = connObj.getConnection()
1122: .createStatement();
1123: ResultSet surveyIdRs = surveyIdStmt
1124: .executeQuery("SELECT SurveyId FROM Surveys WHERE "
1125: + "SurveyName='" + surveyName
1126: + "' AND LanguageCode='" + languageCode + "'");
1127: if (surveyIdRs.next())
1128: surveyId = surveyIdRs.getInt(1);
1129: connObj.closeStatement();
1130: if (surveyId == -1)
1131: throw new RuntimeException(
1132: "Cannot find the specified survey");
1133:
1134: setId(surveyId);
1135:
1136: // retrieve the questions
1137: groups.removeAllElements();
1138:
1139: Statement groupsStmt = connObj.getConnection()
1140: .createStatement();
1141: ResultSet groupsRs = groupsStmt
1142: .executeQuery("SELECT * FROM Groups "
1143: + "WHERE SurveyID=" + surveyId
1144: + " ORDER BY Pos");
1145: while (groupsRs.next()) {
1146: int groupId = groupsRs.getInt(1);
1147: int nextGroupId = groupsRs.getInt(2);
1148: String groupName = groupsRs.getString(3);
1149: QuestionGroup currentGroup = new QuestionGroup(groupId,
1150: nextGroupId, groupName);
1151: addGroup(currentGroup);
1152:
1153: Statement questionsStmt = connObj.getConnection()
1154: .createStatement();
1155: ResultSet questionsRs = questionsStmt
1156: .executeQuery("SELECT * FROM Questions WHERE "
1157: + "GroupID=" + groupId + " AND SurveyID="
1158: + surveyId + " ORDER BY Pos");
1159: while (questionsRs.next()) {
1160: int questionId = questionsRs.getInt(1);
1161: String questionText = questionsRs.getString(2);
1162: int questionType = questionsRs.getInt(3);
1163: Question currentQuestion = new Question(questionId,
1164: currentGroup, questionType, questionText);
1165: currentGroup.addQuestion(currentQuestion);
1166:
1167: Statement optionsStmt = connObj.getConnection()
1168: .createStatement();
1169: ResultSet optionsRs = optionsStmt
1170: .executeQuery("SELECT * FROM Options WHERE "
1171: + "questionID=" + questionId
1172: + " AND SurveyID=" + surveyId
1173: + " ORDER BY Pos");
1174: while (optionsRs.next()) {
1175: int optionId = optionsRs.getInt(1);
1176: String optionText = optionsRs.getString(2);
1177: currentQuestion.addOption(optionId, optionText);
1178: }
1179: optionsStmt.close();
1180:
1181: Statement rulesStmt = connObj.getConnection()
1182: .createStatement();
1183: ResultSet rulesRs = rulesStmt
1184: .executeQuery("SELECT * FROM Rules WHERE "
1185: + "GroupID=" + groupId
1186: + " AND SurveyID=" + surveyId);
1187: if (rulesRs.next()) {
1188: String ruleDesc = rulesRs.getString(1);
1189: XmlElement rulesXml = XmlSource
1190: .read(new StringReader(ruleDesc));
1191: currentGroup.setXmlRules(rulesXml);
1192: }
1193: rulesStmt.close();
1194:
1195: }
1196: questionsStmt.close();
1197:
1198: }
1199: groupsStmt.close();
1200:
1201: restartSurvey();
1202: }
1203:
1204: /**
1205: * Restarts this survey.
1206: */
1207: public void restartSurvey() {
1208: resetSurvey();
1209: XModel model = ((XModel) currentProject.getModel().get(
1210: "xui_state/currentSurvey"));
1211: model.removeChildren();
1212: XModel history = ((XModel) currentProject.getModel().get(
1213: "currentSurveyHistory"));
1214: history.removeChildren();
1215: }
1216:
1217: /**
1218: * Saves the current survey state.
1219: * @param pageName the name of the current page
1220: */
1221: public void saveInHistory(String pageName) {
1222: int groupId = currentGroup.getId();
1223: String path = "currentSurveyHistory/" + currentPageNr;
1224: XModel historyModel = (XModel) currentProject.getModel().get(
1225: path);
1226: historyModel.removeChildren();
1227: path = ("" + groupId + "/" + currentCardNumber + "/" + pageName);
1228: historyModel.get(path);
1229: }
1230:
1231: public void restart() {
1232: groups.removeAllElements();
1233: responseSet = new XResponseSet(name);
1234: }
1235:
1236: /**
1237: * Gets the current response set
1238: * @return the response set
1239: */
1240: public XResponseSet getResponses() {
1241: return responseSet;
1242: }
1243:
1244: public int getGroupIdxById(int groupId) {
1245: int idx = -1;
1246: for (int i = 0; (i < groups.size()) && (idx == -1); i++) {
1247: QuestionGroup group = (QuestionGroup) groups.get(i);
1248: if (group.getId() == groupId)
1249: idx = i;
1250: }
1251: return idx;
1252: }
1253:
1254: /**
1255: * Removes the specified question from this survey
1256: * @param question the question to be removed
1257: */
1258: public void removeQuestion(Question question) {
1259: QuestionGroup group = question.getGroup();
1260: group.getQuestions().remove(question);
1261: }
1262:
1263: /**
1264: * Removes the specified question group from this survey
1265: * @param group the group which is to be removed
1266: */
1267: public void removeGroup(QuestionGroup group) {
1268: groups.remove(group);
1269: }
1270:
1271: /**
1272: * Get the total number of questions if this survey
1273: * @return the number of questions in all groups
1274: */
1275: public int getNumQuestions() {
1276: int numQuestions = 0;
1277: for (int i = 0; i < groups.size(); i++) {
1278: QuestionGroup currentGroup = (QuestionGroup) groups
1279: .elementAt(i);
1280: numQuestions += currentGroup.getNumQuestions();
1281: }
1282: return numQuestions;
1283: }
1284:
1285: }
|