0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/qti/helper/item/ItemHelper12Impl.java $
0003: * $Id: ItemHelper12Impl.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the"License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.tool.assessment.qti.helper.item;
0021:
0022: import java.util.ArrayList;
0023: import java.util.Arrays;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.List;
0028: import java.util.Map;
0029: import java.util.Set;
0030: import java.util.StringTokenizer;
0031:
0032: import org.apache.commons.logging.Log;
0033: import org.apache.commons.logging.LogFactory; //import org.w3c.dom.Document;
0034: import org.w3c.dom.Element;
0035: import org.w3c.dom.Node;
0036:
0037: import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerIfc;
0038: import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemTextIfc;
0039: import org.sakaiproject.tool.assessment.qti.asi.Item;
0040: import org.sakaiproject.tool.assessment.qti.constants.AuthoringConstantStrings;
0041: import org.sakaiproject.tool.assessment.qti.constants.QTIVersion;
0042: import org.sakaiproject.tool.assessment.qti.helper.AuthoringXml;
0043: import org.sakaiproject.tool.assessment.data.ifc.assessment.AnswerFeedbackIfc;
0044:
0045: /**
0046: * <p>Version for QTI 1.2 item XML, significant differences between 1.2 and 2.0</p>
0047: * * @version $Id: ItemHelper12Impl.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
0048: *
0049: * Many methods in Fill in Blank and Numerical Responses(FIN) are identical for now.
0050: * This might change if we want to add random variable, parameterized calculation....
0051: *
0052: */
0053:
0054: public class ItemHelper12Impl extends ItemHelperBase implements
0055: ItemHelperIfc {
0056: private static Log log = LogFactory.getLog(ItemHelper12Impl.class);
0057: private static final String MATCH_XPATH = "item/presentation/flow/response_grp/render_choice";
0058: private static final String NBSP = " ";
0059:
0060: //private Document doc;
0061:
0062: protected String[] itemTypes = AuthoringConstantStrings.itemTypes;
0063: private AuthoringXml authoringXml;
0064: private List allIdents;
0065: private Float currentMaxScore = new Float(0);
0066: private float currentPerItemScore = 0;
0067:
0068: /**
0069: *
0070: */
0071: public ItemHelper12Impl() {
0072: super ();
0073: authoringXml = new AuthoringXml(getQtiVersion());
0074: allIdents = new ArrayList();
0075:
0076: log.debug("ItemHelper12Impl");
0077: }
0078:
0079: protected AuthoringXml getAuthoringXml() {
0080: return authoringXml;
0081: }
0082:
0083: /**
0084: * get the qti version
0085: * @return
0086: */
0087: protected int getQtiVersion() {
0088: return QTIVersion.VERSION_1_2;
0089: }
0090:
0091: /**
0092: * Add maximum score to item XML.
0093: * @param score
0094: * @param itemXml
0095: */
0096: public void addMaxScore(Float score, Item itemXml) {
0097: String xPath = "item/resprocessing/outcomes/decvar/@maxvalue";
0098: if (score == null) {
0099: score = new Float(0);
0100: }
0101: currentMaxScore = score;
0102: updateItemXml(itemXml, xPath, "" + score.toString());
0103: }
0104:
0105: /**
0106: * Add minimum score to item XML
0107: * @param score
0108: * @param itemXml
0109: */
0110: public void addMinScore(Float score, Item itemXml) {
0111: String xPath = "item/resprocessing/outcomes/decvar/@minvalue";
0112: updateItemXml(itemXml, xPath, "0");
0113: }
0114:
0115: /**
0116: * Flags an answer as correct.
0117: * @param correctAnswerLabel the answer that is correct
0118: * @param itemXml the encapsulation of the item xml
0119: */
0120: public void addCorrectAnswer(String correctAnswerLabel, Item itemXml) {
0121: this .flagAnswerCorrect(correctAnswerLabel, itemXml, true);
0122: }
0123:
0124: /**
0125: * Flags an answer as INCORRECT.
0126: * Currently, only used for true false questions.
0127: * @param incorrectAnswerLabel the answer that is NOT correct
0128: * @param itemXml the encapsulation of the item xml
0129: */
0130: public void addIncorrectAnswer(String incorrectAnswerLabel,
0131: Item itemXml) {
0132: this .flagAnswerCorrect(incorrectAnswerLabel, itemXml, false);
0133: }
0134:
0135: /**
0136: * Flags an answer as correct/incorrect.
0137: * @param correctAnswerLabel the answer that is correct
0138: * @param itemXml the encapsulation of the item xml
0139: * @param correct true, or false if not correct
0140: */
0141: public void flagAnswerCorrect(String answerLabel, Item itemXml,
0142: boolean correct) {
0143: if (answerLabel == null) {
0144: answerLabel = "";
0145: }
0146: String flag;
0147: if (correct) {
0148: flag = "Correct";
0149:
0150: } else {
0151: flag = "InCorrect";
0152: }
0153:
0154: String respProcBaseXPath = "item/resprocessing/respcondition";
0155: String respProcCondXPath = "/conditionvar/varequal";
0156: String respProcFeedbackXPath = "/displayfeedback/@linkrefid";
0157:
0158: int respSize = 0;
0159:
0160: // now get each response and flag correct answer
0161: List resp = itemXml.selectNodes(respProcBaseXPath);
0162:
0163: if ((resp != null) && (resp.size() > 0)) {
0164: respSize = resp.size();
0165: }
0166:
0167: for (int i = 1; i <= respSize; i++) {
0168: String index = ("[" + i) + "]";
0169: String answerVar = itemXml.selectSingleValue(
0170: respProcBaseXPath + index + respProcCondXPath,
0171: "element");
0172:
0173: if (answerLabel.equals(answerVar)) //found right displayfeedback
0174: {
0175: String xPath = respProcBaseXPath + index + "/@title";
0176: String xfPath = respProcBaseXPath + index
0177: + respProcFeedbackXPath;
0178: updateItemXml(itemXml, xPath, flag);
0179: updateItemXml(itemXml, xfPath, flag);
0180: break; //done
0181: }
0182: }
0183: }
0184:
0185: /**
0186: * Add/update a response label entry
0187: * @param itemXml
0188: * @param xpath
0189: * @param itemText
0190: * @param isInsert
0191: * @param responseNo
0192: * @param responseLabelIdent
0193: */
0194: public void addResponseEntry(Item itemXml, String xpath,
0195: String value, boolean isInsert, String responseNo,
0196: String responseLabel) {
0197: if (isInsert) {
0198: String nextNode = "response_label[" + responseNo + "]";
0199: itemXml.insertElement(nextNode, xpath, "response_label");
0200: itemXml.add(xpath + "/response_label[" + responseNo + "]",
0201: "material/mattext");
0202: } else {
0203: itemXml.add(xpath, "response_label/material/mattext");
0204: }
0205: try {
0206: // put CDATA around answers
0207: log.debug("putting CDATA around : " + value);
0208: value = "<![CDATA[" + value + "]]>";
0209: itemXml.update(xpath + "/response_label[" + responseNo
0210: + "]/material/mattext", value);
0211: } catch (Exception ex) {
0212: log.error("Cannot update value in addResponselEntry(): "
0213: + ex);
0214: }
0215:
0216: String newPath = xpath + "/response_label[" + responseNo + "]";
0217: itemXml.addAttribute(newPath, "ident");
0218: newPath = xpath + "/response_label[" + responseNo + "]/@ident";
0219: updateItemXml(itemXml, newPath, responseLabel);
0220: }
0221:
0222: /**
0223: * Add Item Feedback for a given response number.
0224: * @param itemXml the item xml
0225: * @param responseNo the numnber
0226: */
0227: private void addItemfeedback(Item itemXml, String value,
0228: boolean isInsert, String responseNo, String responseLabel)
0229:
0230: {
0231: String xpath = "item";
0232:
0233: String nextNode = "itemfeedback[" + responseNo + "]";
0234:
0235: if (isInsert) {
0236: itemXml.insertElement(nextNode, xpath, "itemfeedback");
0237: itemXml.add(xpath + "/itemfeedback[" + responseNo + "]",
0238: "flow_mat/material/mattext");
0239:
0240: /*
0241: itemXml.add(
0242: xpath, "itemfeedback/flow_mat/material/mattext");
0243: */
0244: } else {
0245: }
0246: try {
0247: if (value == null) {
0248: value = "";
0249: }
0250: value = "<![CDATA[" + value + "]]>";
0251: itemXml.update(xpath + "/itemfeedback[" + responseNo
0252: + "]/flow_mat/material/mattext", value);
0253: } catch (Exception ex) {
0254: log
0255: .error("Cannot update value in addItemfeedback(): "
0256: + ex);
0257: }
0258:
0259: String newPath = xpath + "/itemfeedback[" + responseNo + "]";
0260: itemXml.addAttribute(newPath, "ident");
0261: newPath = xpath + "/itemfeedback[" + responseNo + "]/@ident";
0262: String feedbackIdent = responseLabel;
0263: updateItemXml(itemXml, newPath, feedbackIdent);
0264:
0265: }
0266:
0267: /**
0268: * Get the metadata field entry XPath
0269: * @return the XPath
0270: */
0271: public String getMetaXPath() {
0272: String xpath = "item/itemmetadata/qtimetadata";
0273: return xpath;
0274: }
0275:
0276: /**
0277: * Get the metadata field entry XPath for a given label
0278: * @param fieldlabel
0279: * @return the XPath
0280: */
0281: public String getMetaLabelXPath(String fieldlabel) {
0282: String xpath = "item/itemmetadata/qtimetadata/qtimetadatafield/fieldlabel[text()='"
0283: + fieldlabel + "']/following-sibling::fieldentry";
0284: return xpath;
0285: }
0286:
0287: /**
0288: * Get the text for the item
0289: * @param itemXml
0290: * @return the text
0291: */
0292: public String getText(Item itemXml) {
0293: String xpath = "item/presentation/flow/material/mattext";
0294: String itemType = itemXml.getItemType();
0295: if (itemType.equals(AuthoringConstantStrings.MATCHING)) {
0296: xpath = "item/presentation/flow//mattext";
0297: }
0298:
0299: return makeItemNodeText(itemXml, xpath);
0300: }
0301:
0302: /**
0303: * Matching only, sets each source to be matched to.
0304: * It also sets the the matching target.
0305: * @param itemTextList lvalue of matches
0306: * @param itemXml
0307: */
0308: private void setItemTextMatching(List itemTextList, Item itemXml) {
0309: String xpath = MATCH_XPATH;
0310: Map allTargets = new HashMap();
0311: itemXml.add(xpath, "response_label");
0312: String randomNumber = ("" + Math.random()).substring(2);
0313: Iterator iter = itemTextList.iterator();
0314: float itSize = itemTextList.size();
0315:
0316: // just in case we screw up
0317: if (itSize > 0) {
0318: currentPerItemScore = currentMaxScore.floatValue() / itSize;
0319: }
0320: int respCondCount = 0; //used to count the respconditions
0321:
0322: while (iter.hasNext()) {
0323: ItemTextIfc itemText = (ItemTextIfc) iter.next();
0324: String text = itemText.getText();
0325: Long sequence = itemText.getSequence();
0326:
0327: String responseLabelIdent = "MS-" + randomNumber + "-"
0328: + sequence;
0329:
0330: List answerList = itemText.getAnswerArray();
0331: Iterator aiter = answerList.iterator();
0332: int noSources = answerList.size();
0333: while (aiter.hasNext()) {
0334: respCondCount++;
0335: AnswerIfc answer = (AnswerIfc) aiter.next();
0336: String answerText = answer.getText();
0337: String label = answer.getLabel();
0338: Long answerSequence = answer.getSequence();
0339: Boolean correct = answer.getIsCorrect();
0340: String responseFeedback = "";
0341: if (correct.booleanValue()) {
0342: responseFeedback = answer
0343: .getAnswerFeedback(AnswerFeedbackIfc.CORRECT_FEEDBACK);
0344: } else {
0345: responseFeedback = answer
0346: .getAnswerFeedback(AnswerFeedbackIfc.INCORRECT_FEEDBACK);
0347: }
0348:
0349: if (responseFeedback == null) {
0350: responseFeedback = "";
0351: }
0352: String responseNo = ""
0353: + (answerSequence.longValue() - noSources + 1);
0354: String respIdent = "MT-" + randomNumber + "-" + label;
0355:
0356: String respCondNo = "" + respCondCount;
0357: responseFeedback = "<![CDATA[" + responseFeedback
0358: + "]]>";
0359: // add source (addMatchingRespcondition())
0360: if (Boolean.TRUE.equals(correct)) {
0361: log.debug("Matching: matched.");
0362: allIdents.add(respIdent); // put in global (ewww) ident list
0363: allTargets.put(respIdent, answerText);
0364: addMatchingRespcondition(true, itemXml, respCondNo,
0365: respIdent, responseLabelIdent,
0366: responseFeedback);
0367: } else {
0368: log.debug("Matching: NOT matched.");
0369: addMatchingRespcondition(false, itemXml,
0370: respCondNo, respIdent, responseLabelIdent,
0371: responseFeedback);
0372: continue; // we skip adding the response label when false
0373: }
0374: }
0375:
0376: String responseNo = "" + sequence;
0377: addMatchingResponseLabelSource(itemXml, responseNo,
0378: responseLabelIdent, text);
0379: }
0380:
0381: // add targets (addMatchingResponseLabelTarget())
0382: for (int i = 0; i < allIdents.size(); i++) {
0383: String respIdent = (String) allIdents.get(i);
0384: String answerText = (String) allTargets.get(respIdent);
0385: String responseNo = "" + (i + 1);
0386: addMatchingResponseLabelTarget(itemXml, responseNo,
0387: respIdent, answerText);
0388:
0389: }
0390: updateAllSourceMatchGroup(itemXml);
0391: }
0392:
0393: //////////////////////////////////////////////////////////////////////////////
0394: // FILL IN THE BLANK
0395: //////////////////////////////////////////////////////////////////////////////
0396:
0397: /**
0398: * Set the item text.
0399: * This is only valid for FIB,a single item text separated by '{}'.
0400: * @param itemText text to be updated, the syntax is in the form:
0401: * 'roses are {} and violets are {}.' -> 'roses are ',' and violets are ','.'
0402: * @param itemXml
0403: */
0404: private void setItemTextFIB(String fibAns, Item itemXml) {
0405: if ((fibAns != null) && (fibAns.trim().length() > 0)) {
0406: List fibList = parseFillInBlank(fibAns);
0407: Map valueMap = null;
0408: //Set newSet = null;
0409: String mattext = null;
0410: String respStr = null;
0411: String xpath = "item/presentation/flow/flow";
0412: //String position = null;
0413: //String[] responses = null;
0414:
0415: if ((fibList != null) && (fibList.size() > 0)) {
0416:
0417: //List idsAndResponses = new ArrayList();
0418: //1. add Mattext And Responses
0419: for (int i = 0; i < fibList.size(); i++) {
0420:
0421: valueMap = (Map) fibList.get(i);
0422:
0423: if ((valueMap != null) && (valueMap.size() > 0)) {
0424: mattext = (String) valueMap.get("text");
0425: //wrap mattext with cdata
0426: mattext = "<![CDATA[" + mattext + "]]>";
0427:
0428: if (mattext != null) {
0429: //add mattext
0430: itemXml.add(xpath, "material/mattext");
0431: String newXpath = xpath
0432: + "/material["
0433: + (new Integer(i + 1).toString() + "]/mattext");
0434:
0435: updateItemXml(itemXml, newXpath, mattext);
0436: }
0437:
0438: respStr = (String) valueMap.get("ans");
0439:
0440: if (respStr != null) {
0441: //add response_str
0442: itemXml.add(xpath,
0443: "response_str/render_fib");
0444: String newXpath = xpath
0445: + "/response_str["
0446: + (new Integer(i + 1).toString() + "]");
0447:
0448: itemXml.addAttribute(newXpath, "ident");
0449: String ident = "FIB0" + i;
0450: updateItemXml(itemXml,
0451: newXpath + "/@ident", ident);
0452:
0453: itemXml.addAttribute(newXpath,
0454: "rcardinality");
0455: updateItemXml(itemXml, newXpath
0456: + "/@rcardinality", "Ordered");
0457:
0458: newXpath = newXpath + "/render_fib";
0459: itemXml.addAttribute(newXpath, "fibtype");
0460: updateItemXml(itemXml, newXpath
0461: + "/@fibtype", "String");
0462:
0463: itemXml.addAttribute(newXpath, "prompt");
0464: updateItemXml(itemXml, newXpath
0465: + "/@prompt", "Box");
0466:
0467: itemXml.addAttribute(newXpath, "columns");
0468: updateItemXml(itemXml, newXpath
0469: + "/@columns", (new Integer(respStr
0470: .length() + 5).toString()));
0471:
0472: itemXml.addAttribute(newXpath, "rows");
0473: updateItemXml(itemXml, newXpath + "/@rows",
0474: "1");
0475:
0476: // we throw this into our global (ugh) list of idents
0477: allIdents.add(ident);
0478: }
0479: }
0480: }
0481: }
0482: }
0483: }
0484:
0485: /**
0486: * we ensure that answer text between brackets is always nonempty, also that
0487: * starting text is nonempty, we use a non-breaking space for this purpose
0488: * @param fib
0489: * @return
0490: */
0491: private static String padFibWithNonbreakSpacesText(String fib) {
0492:
0493: if (fib.startsWith("{")) {
0494: fib = NBSP + fib;
0495: }
0496: return fib.replaceAll("\\}\\{", "}" + NBSP + "{");
0497: }
0498:
0499: /**
0500: * Special FIB processing.
0501: * @param itemXml
0502: * @param responseCondNo
0503: * @param respIdent
0504: * @param points
0505: * @param responses
0506: * @return
0507: */
0508: private Item addFIBRespconditionNotMutuallyExclusive(Item itemXml,
0509: String responseCondNo, String respIdent, String points,
0510: String[] responses) {
0511: String xpath = "item/resprocessing";
0512: itemXml.add(xpath, "respcondition");
0513: String respCond = "item/resprocessing/respcondition["
0514: + responseCondNo + "]";
0515: itemXml.addAttribute(respCond, "continue");
0516: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0517:
0518: String or = "";
0519:
0520: itemXml.add(respCond, "conditionvar/or");
0521: or = respCond + "/conditionvar/or";
0522:
0523: for (int i = 0; i < responses.length; i++) {
0524: itemXml.add(or, "varequal");
0525: int iString = i + 1;
0526: String varequal = or + "/varequal[" + iString + "]";
0527: itemXml.addAttribute(varequal, "case");
0528: itemXml.addAttribute(varequal, "respident");
0529:
0530: updateItemXml(itemXml, varequal + "/@case", "No");
0531:
0532: updateItemXml(itemXml, varequal + "/@respident", respIdent);
0533: // need to wrap CDATA for responses[i] .
0534: String wrapcdata_response = "<![CDATA[" + responses[i]
0535: + "]]>";
0536: updateItemXml(itemXml, varequal, wrapcdata_response);
0537: }
0538:
0539: //Add setvar
0540: itemXml.add(respCond, "setvar");
0541: itemXml.addAttribute(respCond + "/setvar", "action");
0542:
0543: updateItemXml(itemXml, respCond + "/setvar/@action", "Add");
0544: itemXml.addAttribute(respCond + "/setvar", "varname");
0545:
0546: updateItemXml(itemXml, respCond + "/setvar/@varname", "SCORE");
0547:
0548: updateItemXml(itemXml, respCond + "/setvar", points); // this should be minimum value
0549:
0550: return itemXml;
0551: }
0552:
0553: /**
0554: * Special FIB processing.
0555: * @param itemXml
0556: * @param responseCondNo
0557: * @param respIdents
0558: * @param points
0559: * @param response
0560: * @return
0561: */
0562: private Item addFIBRespconditionMutuallyExclusive(Item itemXml,
0563: String responseCondNo, ArrayList respIdents, String points,
0564: String response) {
0565: String xpath = "item/resprocessing";
0566: itemXml.add(xpath, "respcondition");
0567: String respCond = "item/resprocessing/respcondition["
0568: + responseCondNo + "]";
0569: itemXml.addAttribute(respCond, "continue");
0570:
0571: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0572:
0573: String or = "";
0574: itemXml.add(respCond, "conditionvar/or");
0575: or = respCond + "/conditionvar/or";
0576:
0577: for (int i = 0; i < respIdents.size(); i++) {
0578: int iString = i + 1;
0579:
0580: itemXml.add(or, "varequal");
0581: String varequal = or + "/varequal[" + (i + 1) + "]";
0582: itemXml.addAttribute(varequal, "case");
0583: itemXml.addAttribute(varequal, "respident");
0584:
0585: updateItemXml(itemXml, varequal + "/@case", "No");
0586:
0587: updateItemXml(itemXml, varequal + "/@respident",
0588: (String) respIdents.get(i));
0589: updateItemXml(itemXml, varequal, response);
0590: }
0591:
0592: //Add setvar
0593: itemXml.add(respCond, "setvar");
0594: itemXml.addAttribute(respCond + "/setvar", "action");
0595:
0596: updateItemXml(itemXml, respCond + "/setvar/@action", "Add");
0597: itemXml.addAttribute(respCond + "/setvar", "varname");
0598:
0599: updateItemXml(itemXml, respCond + "/setvar/@varname", "SCORE");
0600:
0601: updateItemXml(itemXml, respCond + "/setvar", points); // this should be minimum value
0602:
0603: return itemXml;
0604: }
0605:
0606: /**
0607: * Special FIB processing.
0608: * @param itemXml
0609: * @param responseCondNo
0610: * @return
0611: */
0612: private Item addFIBRespconditionCorrectFeedback(Item itemXml,
0613: String responseCondNo) {
0614: String xpath = "item/resprocessing";
0615: itemXml.add(xpath, "respcondition");
0616: String respCond = "item/resprocessing/respcondition["
0617: + responseCondNo + "]";
0618: itemXml.addAttribute(respCond, "continue");
0619:
0620: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0621:
0622: String and = "";
0623:
0624: itemXml.add(respCond, "conditionvar/and");
0625: and = respCond + "/conditionvar/and";
0626:
0627: for (int i = 1; i < (new Integer(responseCondNo)).intValue(); i++) {
0628: List or = itemXml
0629: .selectNodes("item/resprocessing/respcondition["
0630: + i + "]/conditionvar/or");
0631: if (or != null) {
0632: itemXml.addElement(and, ((Element) or.get(0)));
0633: }
0634: }
0635:
0636: //Add display feedback
0637: itemXml.add(respCond, "displayfeedback");
0638: itemXml.addAttribute(respCond + "/displayfeedback",
0639: "feedbacktype");
0640:
0641: updateItemXml(itemXml, respCond
0642: + "/displayfeedback/@feedbacktype", "Response");
0643: itemXml
0644: .addAttribute(respCond + "/displayfeedback",
0645: "linkrefid");
0646:
0647: updateItemXml(itemXml,
0648: respCond + "/displayfeedback/@linkrefid", "Correct");
0649: return itemXml;
0650: }
0651:
0652: /**
0653: * Special FIB processing.
0654: * @param itemXml
0655: * @param responseCondNo
0656: * @return
0657: */
0658: private Item addFIBRespconditionInCorrectFeedback(Item itemXml,
0659: String responseCondNo) {
0660: String xpath = "item/resprocessing";
0661: itemXml.add(xpath, "respcondition");
0662: String respCond = "item/resprocessing/respcondition["
0663: + responseCondNo + "]";
0664: itemXml.addAttribute(respCond, "continue");
0665:
0666: updateItemXml(itemXml, respCond + "/@continue", "No");
0667:
0668: itemXml.add(respCond, "conditionvar/other");
0669:
0670: // Add display feedback
0671: itemXml.add(respCond, "displayfeedback");
0672: itemXml.addAttribute(respCond + "/displayfeedback",
0673: "feedbacktype");
0674:
0675: updateItemXml(itemXml, respCond
0676: + "/displayfeedback/@feedbacktype", "Response");
0677: itemXml
0678: .addAttribute(respCond + "/displayfeedback",
0679: "linkrefid");
0680:
0681: updateItemXml(itemXml,
0682: respCond + "/displayfeedback/@linkrefid", "InCorrect");
0683: return itemXml;
0684: }
0685:
0686: //////////////////////////////////////////////////////////////////////////////
0687: // Numeric Response
0688: //////////////////////////////////////////////////////////////////////////////
0689:
0690: /**
0691: * Set the item text.
0692: * This is only valid for FIN,a single item text separated by '{}'.
0693: * @param itemText text to be updated, the syntax is in the form:
0694: * 'roses are {} and violets are {}.' -> 'roses are ',' and violets are ','.'
0695: * @param itemXml
0696: */
0697: private void setItemTextFIN(String finAns, Item itemXml) {
0698: if ((finAns != null) && (finAns.trim().length() > 0)) {
0699: List finList = parseFillInNumeric(finAns);
0700: Map valueMap = null;
0701: Set newSet = null;
0702: String mattext = null;
0703: String respStr = null;
0704: String xpath = "item/presentation/flow/flow";
0705: String position = null;
0706: String[] responses = null;
0707:
0708: if ((finList != null) && (finList.size() > 0)) {
0709:
0710: List idsAndResponses = new ArrayList();
0711: //1. add Mattext And Responses
0712: for (int i = 0; i < finList.size(); i++) {
0713:
0714: valueMap = (Map) finList.get(i);
0715:
0716: if ((valueMap != null) && (valueMap.size() > 0)) {
0717: mattext = (String) valueMap.get("text");
0718: // wrap mattext with cdata
0719: mattext = "<![CDATA[" + mattext + "]]>";
0720:
0721: if (mattext != null) {
0722: //add mattext
0723: itemXml.add(xpath, "material/mattext");
0724: String newXpath = xpath
0725: + "/material["
0726: + (new Integer(i + 1).toString() + "]/mattext");
0727:
0728: updateItemXml(itemXml, newXpath, mattext);
0729: }
0730:
0731: respStr = (String) valueMap.get("ans");
0732:
0733: if (respStr != null) {
0734: //add response_str
0735: itemXml.add(xpath,
0736: "response_str/render_fin");
0737: String newXpath = xpath
0738: + "/response_str["
0739: + (new Integer(i + 1).toString() + "]");
0740:
0741: itemXml.addAttribute(newXpath, "ident");
0742: String ident = "FIN0" + i;
0743: updateItemXml(itemXml,
0744: newXpath + "/@ident", ident);
0745:
0746: itemXml.addAttribute(newXpath,
0747: "rcardinality");
0748: updateItemXml(itemXml, newXpath
0749: + "/@rcardinality", "Ordered");
0750:
0751: newXpath = newXpath + "/render_fin";
0752: itemXml.addAttribute(newXpath, "fintype");
0753: updateItemXml(itemXml, newXpath
0754: + "/@fintype", "String");
0755:
0756: itemXml.addAttribute(newXpath, "prompt");
0757: updateItemXml(itemXml, newXpath
0758: + "/@prompt", "Box");
0759:
0760: itemXml.addAttribute(newXpath, "columns");
0761: updateItemXml(itemXml, newXpath
0762: + "/@columns", (new Integer(respStr
0763: .length() + 5).toString()));
0764:
0765: itemXml.addAttribute(newXpath, "rows");
0766: updateItemXml(itemXml, newXpath + "/@rows",
0767: "1");
0768:
0769: // we throw this into our global (ugh) list of idents
0770: allIdents.add(ident);
0771: }
0772: }
0773: }
0774: }
0775: }
0776: }
0777:
0778: /**
0779: * we ensure that answer text between brackets is always nonempty, also that
0780: * starting text is nonempty, we use a non-breaking space for this purpose
0781: * @param fin
0782: * @return
0783: */
0784: private static String padFinWithNonbreakSpacesText(String fin) {
0785:
0786: if (fin.startsWith("{")) {
0787: fin = NBSP + fin;
0788: }
0789: return fin.replaceAll("\\}\\{", "}" + NBSP + "{");
0790: }
0791:
0792: /**
0793: * Special FIN processing.
0794: * @param itemXml
0795: * @param responseCondNo
0796: * @param respIdent
0797: * @param points
0798: * @param responses
0799: * @return
0800: */
0801: private Item addFINRespconditionNotMutuallyExclusive(Item itemXml,
0802: String responseCondNo, String respIdent, String points,
0803: String[] responses) {
0804: String xpath = "item/resprocessing";
0805: itemXml.add(xpath, "respcondition");
0806: String respCond = "item/resprocessing/respcondition["
0807: + responseCondNo + "]";
0808: itemXml.addAttribute(respCond, "continue");
0809: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0810:
0811: String or = "";
0812:
0813: itemXml.add(respCond, "conditionvar/or");
0814: or = respCond + "/conditionvar/or";
0815:
0816: for (int i = 0; i < responses.length; i++) {
0817: itemXml.add(or, "varequal");
0818: int iString = i + 1;
0819: String varequal = or + "/varequal[" + iString + "]";
0820: itemXml.addAttribute(varequal, "case");
0821: itemXml.addAttribute(varequal, "respident");
0822:
0823: updateItemXml(itemXml, varequal + "/@case", "No");
0824:
0825: updateItemXml(itemXml, varequal + "/@respident", respIdent);
0826: // need to wrap CDATA for responses[i] .
0827: String wrapcdata_response = "<![CDATA[" + responses[i]
0828: + "]]>";
0829:
0830: updateItemXml(itemXml, varequal, wrapcdata_response);
0831: }
0832:
0833: //Add setvar
0834: itemXml.add(respCond, "setvar");
0835: itemXml.addAttribute(respCond + "/setvar", "action");
0836:
0837: updateItemXml(itemXml, respCond + "/setvar/@action", "Add");
0838: itemXml.addAttribute(respCond + "/setvar", "varname");
0839:
0840: updateItemXml(itemXml, respCond + "/setvar/@varname", "SCORE");
0841:
0842: updateItemXml(itemXml, respCond + "/setvar", points); // this should be minimum value
0843:
0844: return itemXml;
0845: }
0846:
0847: /**
0848: * Special FIN processing.
0849: * @param itemXml
0850: * @param responseCondNo
0851: * @param respIdents
0852: * @param points
0853: * @param response
0854: * @return
0855: */
0856: private Item addFINRespconditionMutuallyExclusive(Item itemXml,
0857: String responseCondNo, ArrayList respIdents, String points,
0858: String response) {
0859: String xpath = "item/resprocessing";
0860: itemXml.add(xpath, "respcondition");
0861: String respCond = "item/resprocessing/respcondition["
0862: + responseCondNo + "]";
0863: itemXml.addAttribute(respCond, "continue");
0864:
0865: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0866:
0867: String or = "";
0868: itemXml.add(respCond, "conditionvar/or");
0869: or = respCond + "/conditionvar/or";
0870:
0871: for (int i = 0; i < respIdents.size(); i++) {
0872: int iString = i + 1;
0873:
0874: itemXml.add(or, "varequal");
0875: String varequal = or + "/varequal[" + (i + 1) + "]";
0876: itemXml.addAttribute(varequal, "case");
0877: itemXml.addAttribute(varequal, "respident");
0878:
0879: updateItemXml(itemXml, varequal + "/@case", "No");
0880:
0881: updateItemXml(itemXml, varequal + "/@respident",
0882: (String) respIdents.get(i));
0883: updateItemXml(itemXml, varequal, response);
0884: }
0885:
0886: //Add setvar
0887: itemXml.add(respCond, "setvar");
0888: itemXml.addAttribute(respCond + "/setvar", "action");
0889:
0890: updateItemXml(itemXml, respCond + "/setvar/@action", "Add");
0891: itemXml.addAttribute(respCond + "/setvar", "varname");
0892:
0893: updateItemXml(itemXml, respCond + "/setvar/@varname", "SCORE");
0894:
0895: updateItemXml(itemXml, respCond + "/setvar", points); // this should be minimum value
0896:
0897: return itemXml;
0898: }
0899:
0900: /**
0901: * Special FIN processing.
0902: * @param itemXml
0903: * @param responseCondNo
0904: * @return
0905: */
0906: private Item addFINRespconditionCorrectFeedback(Item itemXml,
0907: String responseCondNo) {
0908: String xpath = "item/resprocessing";
0909: itemXml.add(xpath, "respcondition");
0910: String respCond = "item/resprocessing/respcondition["
0911: + responseCondNo + "]";
0912: itemXml.addAttribute(respCond, "continue");
0913:
0914: updateItemXml(itemXml, respCond + "/@continue", "Yes");
0915:
0916: String and = "";
0917:
0918: itemXml.add(respCond, "conditionvar/and");
0919: and = respCond + "/conditionvar/and";
0920:
0921: for (int i = 1; i < (new Integer(responseCondNo)).intValue(); i++) {
0922: List or = itemXml
0923: .selectNodes("item/resprocessing/respcondition["
0924: + i + "]/conditionvar/or");
0925: if (or != null) {
0926: itemXml.addElement(and, ((Element) or.get(0)));
0927: }
0928: }
0929:
0930: //Add display feedback
0931: itemXml.add(respCond, "displayfeedback");
0932: itemXml.addAttribute(respCond + "/displayfeedback",
0933: "feedbacktype");
0934:
0935: updateItemXml(itemXml, respCond
0936: + "/displayfeedback/@feedbacktype", "Response");
0937: itemXml
0938: .addAttribute(respCond + "/displayfeedback",
0939: "linkrefid");
0940:
0941: updateItemXml(itemXml,
0942: respCond + "/displayfeedback/@linkrefid", "Correct");
0943: return itemXml;
0944: }
0945:
0946: /**
0947: * Special FIN processing.
0948: * @param itemXml
0949: * @param responseCondNo
0950: * @return
0951: */
0952: private Item addFINRespconditionInCorrectFeedback(Item itemXml,
0953: String responseCondNo) {
0954: String xpath = "item/resprocessing";
0955: itemXml.add(xpath, "respcondition");
0956: String respCond = "item/resprocessing/respcondition["
0957: + responseCondNo + "]";
0958: itemXml.addAttribute(respCond, "continue");
0959:
0960: updateItemXml(itemXml, respCond + "/@continue", "No");
0961:
0962: itemXml.add(respCond, "conditionvar/other");
0963:
0964: // Add display feedback
0965: itemXml.add(respCond, "displayfeedback");
0966: itemXml.addAttribute(respCond + "/displayfeedback",
0967: "feedbacktype");
0968:
0969: updateItemXml(itemXml, respCond
0970: + "/displayfeedback/@feedbacktype", "Response");
0971: itemXml
0972: .addAttribute(respCond + "/displayfeedback",
0973: "linkrefid");
0974:
0975: updateItemXml(itemXml,
0976: respCond + "/displayfeedback/@linkrefid", "InCorrect");
0977: return itemXml;
0978: }
0979:
0980: /**
0981: *
0982: * @param idsAndResponses
0983: * @return
0984: */
0985: private ArrayList getSimilarCorrectAnswerIDs(List idsAndResponses) {
0986: String[] compareResponse = null;
0987: ArrayList finalArray = new ArrayList(); // this is list of maps which contains arrayList correct responses and ids
0988: ArrayList idList = new ArrayList();
0989: //ArrayList responseList = new ArrayList();
0990: Map intermediaryMap = new HashMap();
0991: for (int l = 0; l < idsAndResponses.size(); l++) {
0992: Map idsAndResponsesMap = (Map) idsAndResponses.get(l);
0993: Set set = idsAndResponsesMap.keySet();
0994: Iterator keys = set.iterator();
0995: while (keys.hasNext()) {
0996: String respIdent = (String) keys.next();
0997: String[] responses = null;
0998: if ((respIdent != null) && (respIdent.length() > 0)) {
0999: responses = (String[]) idsAndResponsesMap
1000: .get(respIdent);
1001: }
1002:
1003: boolean newElement = true;
1004: for (int i = 0; i < finalArray.size(); i++) {
1005: Map currentEntry = (Map) finalArray.get(i);
1006: Set entrySet = currentEntry.keySet();
1007: Iterator entrykeys = entrySet.iterator();
1008: while (entrykeys.hasNext()) {
1009: compareResponse = (String[]) entrykeys.next();
1010: if (Arrays.equals(responses, compareResponse)) {
1011: idList = (ArrayList) currentEntry
1012: .get(compareResponse);
1013: idList.add(respIdent);
1014: newElement = false;
1015: }
1016: }
1017: }
1018:
1019: if ((finalArray.size() == 0) || (newElement)) {
1020: idList = new ArrayList();
1021: idList.add(respIdent);
1022: intermediaryMap = new HashMap();
1023: intermediaryMap.put(responses, idList);
1024: finalArray.add(intermediaryMap);
1025: }
1026: }
1027: }
1028: return finalArray;
1029: }
1030:
1031: /**
1032: * Special FIB processing.
1033: * @param itemXml
1034: * @param idsAndResponses
1035: * @param allIdents
1036: * @param isMutuallyExclusive
1037: * @param points
1038: * @return
1039: */
1040: private Item addFIBRespconditions(Item itemXml,
1041: List idsAndResponses, List allIdents,
1042: boolean isMutuallyExclusive, String points) {
1043: if (idsAndResponses.size() > 0) {
1044: ArrayList combinationResponses = getSimilarCorrectAnswerIDs(idsAndResponses);
1045: if (combinationResponses != null
1046: && combinationResponses.size() > 0) {
1047: int respConditionNo = 1;
1048: for (int i = 0; i < combinationResponses.size(); i++) {
1049: Map currentEntry = (Map) combinationResponses
1050: .get(i);
1051: Set entrySet = currentEntry.keySet();
1052: Iterator entrykeys = entrySet.iterator();
1053: while (entrykeys.hasNext()) {
1054: String[] responses = (String[]) entrykeys
1055: .next();
1056: ArrayList idList = (ArrayList) currentEntry
1057: .get(responses);
1058: if (idList != null && idList.size() > 0) {
1059: if (idList.size() == 1) {
1060: addFIBRespconditionNotMutuallyExclusive(
1061: itemXml, new Integer(
1062: respConditionNo)
1063: .toString(),
1064: (String) idList.get(0), points,
1065: responses);
1066: respConditionNo = respConditionNo + 1;
1067: } else {
1068: for (int k = 0; k < responses.length; k++) {
1069:
1070: addFIBRespconditionMutuallyExclusive(
1071: itemXml, new Integer(
1072: respConditionNo)
1073: .toString(),
1074: idList, points,
1075: responses[k]);
1076: respConditionNo = respConditionNo + 1;
1077:
1078: }
1079:
1080: }
1081: }
1082: }
1083: }
1084: // add respcondition for all correct answers
1085: addFIBRespconditionCorrectFeedback(itemXml,
1086: new Integer(respConditionNo).toString());
1087: respConditionNo = respConditionNo + 1;
1088: //add respcondition for all incorrect answers
1089: addFIBRespconditionInCorrectFeedback(itemXml,
1090: new Integer(respConditionNo).toString());
1091: }
1092:
1093: }
1094: return itemXml;
1095: }
1096:
1097: /**
1098: * Special FIN processing.
1099: * @param itemXml
1100: * @param idsAndResponses
1101: * @param allIdents
1102: * @param isMutuallyExclusive
1103: * @param points
1104: * @return
1105: */
1106:
1107: private Item addFINRespconditions(Item itemXml,
1108: List idsAndResponses, List allIdents,
1109: boolean isMutuallyExclusive, String points) {
1110: if (idsAndResponses.size() > 0) {
1111: ArrayList combinationResponses = getSimilarCorrectAnswerIDs(idsAndResponses);
1112: if (combinationResponses != null
1113: && combinationResponses.size() > 0) {
1114: int respConditionNo = 1;
1115: for (int i = 0; i < combinationResponses.size(); i++) {
1116: Map currentEntry = (Map) combinationResponses
1117: .get(i);
1118: Set entrySet = currentEntry.keySet();
1119: Iterator entrykeys = entrySet.iterator();
1120: while (entrykeys.hasNext()) {
1121: String[] responses = (String[]) entrykeys
1122: .next();
1123: ArrayList idList = (ArrayList) currentEntry
1124: .get(responses);
1125: if (idList != null && idList.size() > 0) {
1126: if (idList.size() == 1) {
1127: addFINRespconditionNotMutuallyExclusive(
1128: itemXml, new Integer(
1129: respConditionNo)
1130: .toString(),
1131: (String) idList.get(0), points,
1132: responses);
1133: respConditionNo = respConditionNo + 1;
1134: } else {
1135: for (int k = 0; k < responses.length; k++) {
1136:
1137: addFINRespconditionMutuallyExclusive(
1138: itemXml, new Integer(
1139: respConditionNo)
1140: .toString(),
1141: idList, points,
1142: responses[k]);
1143: respConditionNo = respConditionNo + 1;
1144:
1145: }
1146:
1147: }
1148: }
1149: }
1150: }
1151: // add respcondition for all correct answers
1152: addFINRespconditionCorrectFeedback(itemXml,
1153: new Integer(respConditionNo).toString());
1154: respConditionNo = respConditionNo + 1;
1155: // add respcondition for all incorrect answers
1156: addFINRespconditionInCorrectFeedback(itemXml,
1157: new Integer(respConditionNo).toString());
1158: }
1159:
1160: }
1161: return itemXml;
1162: }
1163:
1164: /**
1165: * Get list of form:
1166: * {ans=red, text=Roses are},
1167: * {ans=blue, text=and violets are},
1168: * {ans=null, text=.}
1169: * From String of form "Roses are {red} and violets are {blue}."
1170: *
1171: * @param input
1172: * @return list of Maps
1173: */
1174: private static List parseFillInBlank(String input) {
1175: input = padFibWithNonbreakSpacesText(input);
1176:
1177: Map tempMap = null;
1178: List storeParts = new ArrayList();
1179: if (input == null) {
1180: return storeParts;
1181: }
1182:
1183: StringTokenizer st = new StringTokenizer(input, "}");
1184: String tempToken = "";
1185: String[] splitArray = null;
1186:
1187: while (st.hasMoreTokens()) {
1188: tempToken = st.nextToken();
1189: tempMap = new HashMap();
1190:
1191: //split out text and answer parts from token
1192: splitArray = tempToken.trim().split("\\{", 2);
1193: tempMap.put("text", splitArray[0].trim());
1194: if (splitArray.length > 1) {
1195:
1196: tempMap.put("ans", (splitArray[1]));
1197: } else {
1198: tempMap.put("ans", null);
1199: }
1200:
1201: storeParts.add(tempMap);
1202: }
1203:
1204: return storeParts;
1205: }
1206:
1207: private static List parseFillInNumeric(String input) {
1208: input = padFinWithNonbreakSpacesText(input);
1209:
1210: Map tempMap = null;
1211: List storeParts = new ArrayList();
1212: if (input == null) {
1213: return storeParts;
1214: }
1215:
1216: StringTokenizer st = new StringTokenizer(input, "}");
1217: String tempToken = "";
1218: String[] splitArray = null;
1219:
1220: while (st.hasMoreTokens()) {
1221: tempToken = st.nextToken();
1222: tempMap = new HashMap();
1223:
1224: //split out text and answer parts from token
1225: splitArray = tempToken.trim().split("\\{", 2);
1226: tempMap.put("text", splitArray[0].trim());
1227: if (splitArray.length > 1) {
1228:
1229: tempMap.put("ans", (splitArray[1]));
1230: } else {
1231: tempMap.put("ans", null);
1232: }
1233:
1234: storeParts.add(tempMap);
1235: }
1236:
1237: return storeParts;
1238: }
1239:
1240: private static String[] getPossibleCorrectResponses(String inputStr) {
1241: String patternStr = ",";
1242: String[] responses = inputStr.split(patternStr);
1243: for (int i = 0; i < responses.length; i++) {
1244: responses[i] = responses[i].trim();
1245: }
1246: Arrays.sort(responses);
1247:
1248: return responses;
1249: }
1250:
1251: /**
1252: * Set the item text.
1253: * This is valid for all undelimited single item texts.
1254: * Not valid for matching or fill in the blank, but OK for instructional text
1255: * @param itemText text to be updated
1256: * @param itemXml
1257: */
1258: public void setItemText(String itemText, Item itemXml) {
1259: String xpath = "item/presentation/flow/material/mattext";
1260:
1261: List list = itemXml.selectNodes(xpath);
1262: log.debug("in ItemHelper12Impl.java: setItemText() text = "
1263: + itemText);
1264: itemText = "<![CDATA[" + itemText + "]]>";
1265: log
1266: .debug("in ItemHelperBase.java: setItemText() wrapped CDATA text is = "
1267: + itemText);
1268:
1269: try {
1270: itemXml.update(xpath, itemText);
1271: } catch (Exception ex) {
1272: log.error(ex.getMessage(), ex);
1273: }
1274: }
1275:
1276: /**
1277: * Set the (one or more) item texts.
1278: * Valid for single and multiple texts.
1279: * @param itemXml
1280: * @param itemText text to be updated
1281: */
1282: public void setItemTexts(ArrayList itemTextList, Item itemXml) {
1283: if (itemTextList.size() < 1) {
1284: return;
1285: }
1286:
1287: if (itemXml.isMatching()) {
1288: setItemTextMatching(itemTextList, itemXml);
1289: return;
1290: }
1291:
1292: String text = ((ItemTextIfc) itemTextList.get(0)).getText();
1293: if (itemXml.isFIB()) {
1294: setItemTextFIB(text, itemXml);
1295: return;
1296: } else if (itemXml.isFIN()) {
1297: setItemTextFIN(text, itemXml);
1298: return;
1299: } else {
1300: setItemText(text, itemXml);
1301: return;
1302: }
1303:
1304: }
1305:
1306: /**
1307: * get item type string
1308: * @param itemXml
1309: * @return type as string
1310: */
1311: public String getItemType(Item itemXml)
1312:
1313: {
1314: String type = itemXml.getFieldentry("qmd_itemtype");
1315:
1316: return type;
1317: }
1318:
1319: /**
1320: * Set the answer texts for item.
1321: * @param itemTextList the text(s) for item
1322: */
1323:
1324: public void setAnswers(ArrayList itemTextList, Item itemXml) {
1325:
1326: log.debug("entered setAnswers()");
1327: log.debug("size=" + itemTextList.size());
1328: // other types either have no answer or include them in their template, or,
1329: // in matching, generate all in setItemTextMatching()
1330: if (!itemXml.isFIB() && !itemXml.isMCSC() && !itemXml.isFIN()
1331: && !itemXml.isMCMC() && !itemXml.isEssay()) {
1332: return;
1333: }
1334:
1335: // OK, so now we are in business.
1336: String xpath = "item/presentation/flow/response_lid/render_choice";
1337:
1338: List list = itemXml.selectNodes(xpath);
1339: Iterator nodeIter = list.iterator();
1340:
1341: Iterator iter = itemTextList.iterator();
1342: Set answerSet = new HashSet();
1343:
1344: char label = 'A';
1345: int xpathIndex = 1;
1346: int respIdentCount = 0;
1347: while (iter.hasNext()) {
1348: answerSet = ((ItemTextIfc) iter.next()).getAnswerSet();
1349: log.debug("answersize=" + answerSet.size());
1350: //System.out.println("answersize=" + answerSet.size());
1351: Iterator aiter = answerSet.iterator();
1352: while (aiter.hasNext()) {
1353: AnswerIfc answer = (AnswerIfc) aiter.next();
1354: if (Boolean.TRUE.equals(answer.getIsCorrect())) {
1355: this .addCorrectAnswer("" + label, itemXml);
1356: }
1357: String value = answer.getText();
1358: log.debug("\n\n***The answer is: " + value);
1359: //System.out.println("\n\n***The answer is: " + value);
1360: // if and only if FIB we do special processing
1361: if (itemXml.isFIB()) {
1362: String[] responses = { value }; // one possible for now
1363: String respIdent = (String) allIdents
1364: .get(respIdentCount++);
1365: addFIBRespconditionNotMutuallyExclusive(itemXml, ""
1366: + xpathIndex, respIdent, "0", responses);
1367: label++;
1368: xpathIndex++;
1369: continue; //
1370: }
1371:
1372: if (itemXml.isFIN()) {
1373: String[] responses = { value }; // one possible for now
1374: String respIdent = (String) allIdents
1375: .get(respIdentCount++);
1376: addFINRespconditionNotMutuallyExclusive(itemXml, ""
1377: + xpathIndex, respIdent, "0", responses);
1378: label++;
1379: xpathIndex++;
1380: continue; //
1381: }
1382:
1383: // process into XML
1384: // we assume that we have equal to or more than the requisite elements
1385: // if we have more than the existing elements we manufacture more
1386: // with labels 'A', 'B'....etc.
1387: Node node = null;
1388: try {
1389: boolean isInsert = true;
1390: if (nodeIter.hasNext()) {
1391: isInsert = false;
1392: }
1393: this .addResponseEntry(itemXml, xpath, value,
1394: isInsert, "" + xpathIndex, "" + label);
1395: } catch (Exception ex) {
1396: log.error("Cannot process source document.", ex);
1397: }
1398:
1399: label++;
1400: xpathIndex++;
1401: }
1402: }
1403: }
1404:
1405: /**
1406: * Set the feedback texts for item.
1407: * @param itemTextList the text(s) for item
1408: * @param itemXml
1409: */
1410:
1411: public void setFeedback(ArrayList itemTextList, Item itemXml) {
1412: //log.info("setFeedback()");
1413:
1414: boolean hasAnswerLevelFeedback = itemXml.isMCMC()
1415: || itemXml.isMCSC();
1416: //log.info("itemXml.getItemType(): " + itemXml.getItemType());
1417: //log.info("hasAnswerLevelFeedback: " + hasAnswerLevelFeedback);
1418:
1419: // for any answers that are now in the template, create a feedback
1420: String xpath = "item/itemfeedback/flow/response_lid/render_choice";
1421: int xpathIndex = 1;
1422:
1423: List list = itemXml.selectNodes(xpath);
1424: Iterator nodeIter = list.iterator();
1425:
1426: Iterator iter = itemTextList.iterator();
1427: Set answerSet = new HashSet();
1428:
1429: char label = 'A';
1430: boolean first = true;
1431: while (iter.hasNext()) {
1432: ItemTextIfc itemTextIfc = (ItemTextIfc) iter.next();
1433:
1434: if (first) // then do once
1435: {
1436: addCorrectAndIncorrectFeedback(itemXml, itemTextIfc);
1437: xpathIndex = 1;
1438: first = false;
1439: }
1440:
1441: if (hasAnswerLevelFeedback) {
1442: log.debug("Setting answer level feedback");
1443:
1444: answerSet = itemTextIfc.getAnswerSet();
1445: log.debug("answerSet.size(): " + answerSet.size());
1446:
1447: Iterator aiter = answerSet.iterator();
1448: while (aiter.hasNext()) {
1449: AnswerIfc answer = (AnswerIfc) aiter.next();
1450: //log.info("Setting answer feedback for: " + answer.getText());
1451: //log.info("xpathIndex: " + xpathIndex);
1452: //log.info("label: " + label);
1453:
1454: String value = answer.getGeneralAnswerFeedback();
1455: boolean isInsert = true;
1456: if (nodeIter.hasNext()) {
1457: isInsert = false;
1458: }
1459:
1460: addAnswerFeedback(itemXml, value, isInsert,
1461: xpathIndex, "" + label);
1462: label++;
1463: xpathIndex++;
1464: }
1465: }
1466:
1467: addGeneralFeedback(itemXml, xpathIndex, itemTextIfc);
1468: }
1469:
1470: }
1471:
1472: /**
1473: * Adds feedback with idents of Correct and InCorrect
1474: * @param itemXml
1475: * @param itemTextIfc
1476: */
1477:
1478: private void addCorrectAndIncorrectFeedback(Item itemXml,
1479: ItemTextIfc itemTextIfc) {
1480: String correctFeedback = itemTextIfc.getItem()
1481: .getCorrectItemFeedback();
1482: String incorrectFeedback = itemTextIfc.getItem()
1483: .getInCorrectItemFeedback();
1484: log.debug("CORRECT FEEDBACK: " + correctFeedback);
1485: if (correctFeedback != null) {
1486: this .addItemfeedback(itemXml, correctFeedback, false, "1",
1487: "" + "Correct");
1488: }
1489: log.debug("INCORRECT FEEDBACK: " + incorrectFeedback);
1490: if (incorrectFeedback != null) {
1491: this .addItemfeedback(itemXml, incorrectFeedback, false,
1492: "2", "" + "InCorrect");
1493: }
1494: }
1495:
1496: /**
1497: * Adds feedback with ident referencing item ident
1498: * @param itemXml
1499: * @param xpathIndex
1500: * @param itemTextIfc
1501: */
1502: private void addGeneralFeedback(Item itemXml, int xpathIndex,
1503: ItemTextIfc itemTextIfc) {
1504: log.debug("\nDebug add in General Feedback");
1505: String generalFeedback = itemTextIfc.getItem()
1506: .getGeneralItemFeedback();
1507: String itemId = itemTextIfc.getItem().getItemIdString();
1508: if (generalFeedback != null) {
1509: addItemfeedback(itemXml, generalFeedback, true, ""
1510: + xpathIndex++, itemId);
1511: }
1512: }
1513:
1514: /**
1515: * Adds feedback with ident referencing answer ident.
1516: *
1517: * @param itemXml
1518: * @param value
1519: * @param isInsert
1520: * @param responseNo
1521: * @param responseLabel
1522: */
1523: private void addAnswerFeedback(Item itemXml, String value,
1524: boolean isInsert, int responseNo, String responseLabel) {
1525: log.debug("addAnswerFeedback()");
1526: log.debug("answer feedback value: " + value);
1527: if (value == null) {
1528: value = "<![CDATA[]]>";
1529: } else {
1530: value = "<![CDATA[" + value + "]]>";
1531: }
1532: String respCond = "item/resprocessing/respcondition["
1533: + responseNo + "]";
1534: updateItemXml(itemXml, respCond + "/setvar", ""
1535: + currentPerItemScore);
1536: updateItemXml(itemXml, respCond
1537: + "/displayfeedback[2]/@linkrefid", "AnswerFeedback");
1538: updateItemXml(itemXml, respCond + "/displayfeedback[2]", value);
1539: }
1540:
1541: ////////////////////////////////////////////////////////////////
1542: // MATCHING
1543: ////////////////////////////////////////////////////////////////
1544:
1545: /**
1546: * Add the matching response label entry source.
1547: * @param itemXml
1548: * @param responseNo
1549: * @param responseLabelIdent
1550: * @param value
1551: */
1552: private void addMatchingResponseLabelTarget(Item itemXml,
1553: String responseNo, String respIdent, String value) {
1554: String xpath = MATCH_XPATH;
1555: insertResponseLabelMattext(itemXml, responseNo, value, xpath);
1556:
1557: String newPath = xpath + "/response_label[" + responseNo + "]";
1558: itemXml.addAttribute(newPath, "ident");
1559: newPath = xpath + "/response_label[" + responseNo + "]/@ident";
1560: updateItemXml(itemXml, newPath, respIdent);
1561: }
1562:
1563: /**
1564: * Add the matching response label entry source.
1565: * @param itemXml
1566: * @param responseNo
1567: * @param responseLabelIdent
1568: * @param value
1569: */
1570: private void addMatchingResponseLabelSource(Item itemXml,
1571: String responseNo, String responseLabelIdent, String value) {
1572: String xpath = MATCH_XPATH;
1573:
1574: insertResponseLabelMattext(itemXml, responseNo, value, xpath);
1575:
1576: itemXml.addAttribute(xpath + "/response_label[" + responseNo
1577: + "]", "match_max");
1578: itemXml.addAttribute(xpath + "/response_label[" + responseNo
1579: + "]", "match_group");
1580:
1581: updateItemXml(itemXml, xpath + "/response_label[" + responseNo
1582: + "]" + "/@match_max", "1");
1583:
1584: String newPath = xpath + "/response_label[" + responseNo + "]";
1585: itemXml.addAttribute(newPath, "ident");
1586: newPath = xpath + "/response_label[" + responseNo + "]/@ident";
1587: updateItemXml(itemXml, newPath, responseLabelIdent);
1588: }
1589:
1590: /**
1591: * utility method for addMatchingResponseLabelTarget(), addMatchingResponseLabelSource()
1592: * @param itemXml
1593: * @param responseNo
1594: * @param value
1595: * @param xpath
1596: */
1597: private void insertResponseLabelMattext(Item itemXml,
1598: String responseNo, String value, String xpath) {
1599: String nextNode = "response_label[" + responseNo + "]";
1600: itemXml.insertElement(nextNode, xpath, "response_label");
1601: itemXml.add(xpath + "/response_label[" + responseNo + "]",
1602: "material/mattext");
1603: try {
1604:
1605: log
1606: .debug("in ItemHelper12Impl.java: insertResponseLabelMattext() text = "
1607: + value);
1608: value = "<![CDATA[" + value + "]]>";
1609: log
1610: .debug("in ItemHelperBase.java: insertResponseLabelMattext() wrapped CDATA text is = "
1611: + value);
1612:
1613: itemXml.update(xpath + "/response_label[" + responseNo
1614: + "]/material/mattext", value);
1615: } catch (Exception ex) {
1616: log.warn("Unable to set mattext in '" + xpath
1617: + "/response_label[" + responseNo + "]' to '"
1618: + value + "'");
1619: }
1620: }
1621:
1622: /**
1623: * Add the matching item feedback.
1624: *
1625: * @param itemXml
1626: * @param feedbackIdent
1627: * @param responseNo
1628: */
1629: private void addMatchingItemfeedback(Item itemXml,
1630: String feedbackIdent, String responseNo) {
1631: String xpath = "item";
1632:
1633: String nextNode = "itemfeedback[" + responseNo + "]";
1634: itemXml.insertElement(nextNode, xpath, "itemfeedback");
1635: itemXml.add(xpath + "/itemfeedback[" + responseNo + "]",
1636: "flow_mat/material/mattext");
1637:
1638: String newPath = xpath + "/itemfeedback[" + responseNo + "]";
1639: itemXml.addAttribute(newPath, "ident");
1640: newPath = xpath + "/itemfeedback[" + responseNo + "]/@ident";
1641:
1642: updateItemXml(itemXml, newPath, feedbackIdent);
1643:
1644: //Add placeholder for image
1645: xpath = xpath + "/itemfeedback[" + responseNo + "]/flow_mat";
1646: itemXml.add(xpath, "material/matimage");
1647: xpath = xpath + "/flow_mat/material[2]/matimage";
1648:
1649: //Image attributes
1650: itemXml.addAttribute(xpath, "uri");
1651: itemXml.addAttribute(xpath, "imagetype");
1652: xpath = xpath + "/@imagetype";
1653: updateItemXml(itemXml, xpath, "text/html");
1654: }
1655:
1656: /**
1657: * Add matching response condition.
1658: * @param itemXml
1659: * @param responseNo
1660: * @param respident
1661: * @param responseLabelIdent
1662: */
1663: private void addMatchingRespcondition(boolean correct,
1664: Item itemXml, String responseNo, String respident,
1665: String responseLabelIdent, String responseFeedback) {
1666:
1667: String xpath = "item/resprocessing";
1668: itemXml.add(xpath, "respcondition/conditionvar/varequal");
1669:
1670: String respCond = "item/resprocessing/respcondition["
1671: + responseNo + "]";
1672: itemXml.addAttribute(respCond, "continue");
1673: updateItemXml(itemXml, respCond + "/@continue", "No");
1674: itemXml.addAttribute(respCond + "/conditionvar/varequal",
1675: "case");
1676: updateItemXml(itemXml, respCond
1677: + "/conditionvar/varequal/@case", "Yes");
1678: itemXml.addAttribute(respCond + "/conditionvar/varequal",
1679: "respident");
1680: itemXml.addAttribute(respCond + "/conditionvar/varequal",
1681: "index");
1682: updateItemXml(itemXml, respCond
1683: + "/conditionvar/varequal/@index", responseNo);
1684:
1685: if (respident != null) {
1686:
1687: updateItemXml(itemXml, respCond
1688: + "/conditionvar/varequal/@respident", respident);
1689: }
1690:
1691: updateItemXml(itemXml, respCond + "/conditionvar/varequal",
1692: responseLabelIdent);
1693:
1694: //Add setvar
1695: itemXml.add(respCond, "setvar");
1696: itemXml.addAttribute(respCond + "/setvar", "action");
1697: updateItemXml(itemXml, respCond + "/setvar/@action", "Add");
1698: itemXml.addAttribute(respCond + "/setvar", "varname");
1699:
1700: updateItemXml(itemXml, respCond + "/setvar/@varname", "SCORE");
1701:
1702: //Add display feedback
1703:
1704: itemXml.add(respCond, "displayfeedback");
1705: itemXml.addAttribute(respCond + "/displayfeedback",
1706: "feedbacktype");
1707: updateItemXml(itemXml, respCond
1708: + "/displayfeedback/@feedbacktype", "Response");
1709: itemXml
1710: .addAttribute(respCond + "/displayfeedback",
1711: "linkrefid");
1712:
1713: if (correct) {
1714: updateItemXml(itemXml, respCond + "/setvar", ""
1715: + currentPerItemScore);
1716: updateItemXml(itemXml, respCond
1717: + "/displayfeedback/@linkrefid", "CorrectMatch");
1718: } else {
1719: updateItemXml(itemXml, respCond + "/setvar", "0");
1720: updateItemXml(itemXml, respCond
1721: + "/displayfeedback/@linkrefid", "InCorrectMatch");
1722: }
1723:
1724: updateItemXml(itemXml, respCond + "/displayfeedback",
1725: responseFeedback);
1726: }
1727:
1728: /**
1729: * Update match group.
1730: * Uses global internal list of all target idents.
1731: * (... response_label[not(@match_group)]/@ident)
1732: * DO NOT CALL before we have all the target idents ready
1733: * @param itemXml
1734: */
1735: private void updateAllSourceMatchGroup(Item itemXml) {
1736: String matchGroupsXpath = "item/presentation/flow/response_grp/render_choice/response_label[(@match_group)]";
1737:
1738: if (allIdents.size() > 0) {
1739: Iterator iter = allIdents.iterator();
1740: String targetIdent = null;
1741: String match_group = null;
1742: while (iter.hasNext()) {
1743: targetIdent = (String) iter.next();
1744: if (match_group == null) {
1745: match_group = targetIdent;
1746: } else {
1747: match_group = match_group + "," + targetIdent;
1748: }
1749: }
1750:
1751: if (match_group != null) {
1752: int noOfSources = (itemXml
1753: .selectNodes(matchGroupsXpath)).size();
1754: for (int i = 1; i <= noOfSources; i++) {
1755: String xpath = "item/presentation/flow/response_grp/render_choice/response_label["
1756: + i + "]/@match_group";
1757:
1758: updateItemXml(itemXml, xpath, match_group);
1759: }
1760: }
1761: }
1762: }
1763:
1764: }
|