001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/qti/asi/Section.java $
003: * $Id: Section.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the"License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.tool.assessment.qti.asi;
021:
022: import java.util.ArrayList;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Map;
026: import java.util.Random;
027: import java.util.Set;
028:
029: import javax.xml.parsers.DocumentBuilder;
030: import javax.xml.parsers.DocumentBuilderFactory;
031: import javax.xml.parsers.ParserConfigurationException;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.w3c.dom.Document;
036: import org.w3c.dom.Element;
037:
038: import org.sakaiproject.tool.assessment.data.dao.assessment.AttachmentData;
039: import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemDataIfc;
040: import org.sakaiproject.tool.assessment.data.ifc.assessment.ItemMetaDataIfc;
041: import org.sakaiproject.tool.assessment.data.ifc.assessment.SectionDataIfc;
042: import org.sakaiproject.tool.assessment.data.ifc.assessment.SectionMetaDataIfc;
043: import org.sakaiproject.tool.assessment.data.ifc.shared.TypeIfc;
044: import org.sakaiproject.tool.assessment.qti.constants.QTIConstantStrings;
045: import org.sakaiproject.tool.assessment.qti.constants.QTIVersion;
046: import org.sakaiproject.tool.assessment.qti.helper.QTIHelperFactory;
047: import org.sakaiproject.tool.assessment.qti.helper.item.ItemHelperIfc;
048:
049: /**
050: * <p>Copyright: Copyright (c) 2004</p>
051: * <p>Organization: Sakai Project</p>
052: * @author Ed Smiley esmiley@stanford.edu
053: * @author Shastri, Rashmi <rshastri@iupui.edu>
054: * @version $Id: Section.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
055: */
056: public class Section extends ASIBaseClass {
057: private static Log log = LogFactory.getLog(Section.class);
058: public String basePath;
059: private Map items;
060:
061: /**
062: * Explicitly setting serialVersionUID insures future versions can be
063: * successfully restored. It is essential this variable name not be changed
064: * to SERIALVERSIONUID, as the default serialization methods expects this
065: * exact name.
066: */
067: private static final long serialVersionUID = 1;
068: private int qtiVersion;
069:
070: /**
071: * Creates a new Section object.
072: */
073: public Section() {
074: super ();
075: this .basePath = QTIConstantStrings.SECTION;
076: }
077:
078: /**
079: * Creates a new Section object.
080: *
081: * @param document DOCUMENTATION PENDING
082: */
083: public Section(Document document, int qtiVersion) {
084: super (document);
085: if (!QTIVersion.isValid(qtiVersion)) {
086: throw new IllegalArgumentException(
087: "Invalid Section QTI version.");
088: }
089: this .qtiVersion = qtiVersion;
090: this .basePath = QTIConstantStrings.SECTION;
091: }
092:
093: /**
094: * set section ident (id)
095: * @param ident
096: */
097: public void setIdent(String ident) {
098: String xpath = "section";
099: List list = this .selectNodes(xpath);
100: if (list.size() > 0) {
101: Element element = (Element) list.get(0);
102: element.setAttribute("ident", ident);
103: }
104: }
105:
106: /**
107: * set section title
108: * @param title
109: */
110: public void setTitle(String title) {
111: String xpath = "section";
112: List list = this .selectNodes(xpath);
113: if (list.size() > 0) {
114: Element element = (Element) list.get(0);
115: element.setAttribute("title", escapeXml(title));
116: }
117: }
118:
119: /**
120: * Update XML from persistence
121: * @param section
122: */
123: public void update(SectionDataIfc section) {
124: // identity
125: setIdent("" + section.getSectionId());
126: setTitle(section.getTitle());
127: // metadata
128: // Where the heck do these come from? Looks like not being used.
129: // If required we could extract keywords by weighting, and
130: // set rubrics identical to description, or, we could eliminate these from XML.
131:
132: // well, we can add metadata from users' input - lydial
133:
134: setFieldentry("SECTION_INFORMATION", section.getDescription());
135: setFieldentry(
136: "SECTION_OBJECTIVE",
137: section
138: .getSectionMetaDataByLabel(SectionMetaDataIfc.OBJECTIVES));
139: setFieldentry("SECTION_KEYWORD", section
140: .getSectionMetaDataByLabel(SectionMetaDataIfc.KEYWORDS));
141: setFieldentry("SECTION_RUBRIC", section
142: .getSectionMetaDataByLabel(SectionMetaDataIfc.RUBRICS));
143: setFieldentry("ATTACHMENT", getAttachment(section));
144:
145: // items
146: ArrayList items = section.getItemArray();
147: addItems(items);
148: }
149:
150: // /**
151: // * @deprecated hardcoded to support only QTIVersion.VERSION_1_2
152: // * select and order
153: // */
154: // public void selectAndOrder()
155: // {
156: // log.debug("selectAndOrder()");
157: // ArrayList selectedList = this.selectItems();
158: // this.orderItems(selectedList);
159: // ArrayList selectedSections = this.selectSections(basePath);
160: // this.orderSections(basePath, selectedSections, QTIVersion.VERSION_1_2);
161: // }
162:
163: /**
164: * select items
165: *
166: * @return return arraylist of items
167: */
168: private ArrayList selectItems() {
169: log.debug("selectItems()");
170:
171: ArrayList items = new ArrayList();
172:
173: // try
174: // {
175: String xpath = basePath + "/"
176: + QTIConstantStrings.SELECTION_ORDERING + "/";
177: String selectionXPath = xpath + QTIConstantStrings.SELECTION;
178: String orderingXPath = xpath + QTIConstantStrings.ORDER;
179:
180: List selectNodes = this .selectNodes(selectionXPath);
181: List orderNodes = this .selectNodes(orderingXPath);
182:
183: int selectNodeSize = selectNodes.size();
184: for (int i = 0; i < selectNodeSize; i++) {
185: Element selectElement = (Element) selectNodes.get(i);
186: // items.addAll(processSelectElement(selectElement));
187: }
188:
189: if (selectNodeSize == 0) {
190: items.addAll(this .getAllItems());
191: }
192:
193: // }
194: // catch(Exception ex)
195: // {
196: // log.error(ex.getMessage(), ex);
197: // }
198: removeItems();
199:
200: return items;
201: }
202:
203: /**
204: * get all items
205: *
206: * @return list of items
207: */
208: private List getAllItems() {
209: log.debug("getAllItems()");
210:
211: String xpath = basePath + "/" + QTIConstantStrings.ITEM;
212:
213: return this .selectNodes(xpath);
214: }
215:
216: /**
217: * remove items
218: */
219: private void removeItems() {
220: log.debug("removeItems()");
221:
222: String xpath = basePath + "/" + QTIConstantStrings.ITEM;
223: this .removeElement(xpath);
224: }
225:
226: /**
227: * order items
228: *
229: * @param items list of items
230: */
231: private void orderItems(ArrayList items) {
232: if (log.isDebugEnabled()) {
233: log.debug("orderItems(ArrayList " + items + ")");
234: }
235:
236: String xpath = basePath + "/"
237: + QTIConstantStrings.SELECTION_ORDERING + "/";
238: String orderingXPath = xpath + QTIConstantStrings.ORDER;
239: List orderNodes = this .selectNodes(orderingXPath);
240: if ((orderNodes != null) && (orderNodes.size() > 0)) {
241: Element order = (Element) orderNodes.get(0);
242: String orderType = order
243: .getAttribute(QTIConstantStrings.ORDER_TYPE);
244: if ("Random".equalsIgnoreCase(orderType)) {
245: //Randomly order items.
246: long seed = System.currentTimeMillis();
247: Random rand = new Random(seed);
248: int size = items.size();
249: for (int i = 0; i < size; i++) {
250: int randomNum = rand.nextInt(size);
251: Object temp = items.get(i);
252: items.set(i, items.get(randomNum));
253: items.set(randomNum, temp);
254: log
255: .debug("switch item " + i + " with "
256: + randomNum);
257: }
258: }
259: }
260:
261: addItems(items);
262: }
263:
264: /**
265: * Add item list to this section document.
266: *
267: * @param items list of ItemDataIfc
268: */
269: private void addItems(ArrayList items) {
270: if (log.isDebugEnabled()) {
271: log.debug("addItems(ArrayList " + items + ")");
272: }
273: // ItemHelper itemHelper = new ItemHelper();
274: QTIHelperFactory factory = new QTIHelperFactory();
275: ItemHelperIfc itemHelper = factory
276: .getItemHelperInstance(this .qtiVersion);
277:
278: try {
279: String xpath = basePath;
280: for (int i = 0; i < items.size(); i++) {
281: ItemDataIfc item = (ItemDataIfc) items.get(i);
282: //TypeIfc type = item.getType();
283: Long type = item.getTypeId();
284: Item itemXml;
285: if ((TypeIfc.MULTIPLE_CHOICE_SURVEY).equals(type)) {
286: // deprecated, keep it for backward compatibility
287: String scale = item
288: .getItemMetaDataByLabel(ItemMetaDataIfc.SCALENAME);
289: // PREDEFINED_SCALE is the new metadata key
290: if (scale == null) {
291: scale = item
292: .getItemMetaDataByLabel(ItemMetaDataIfc.PREDEFINED_SCALE);
293: }
294: itemXml = itemHelper.readTypeSurveyItem(scale);
295: } else {
296: itemXml = itemHelper.readTypeXMLItem(type);
297: }
298:
299: // update item data
300: itemXml.setIdent(item.getItemIdString());
301: itemXml.update(item);
302: Element itemElement = (Element) itemXml.getDocument()
303: .getDocumentElement();
304: log.debug("Item ident is: "
305: + itemElement.getAttribute("ident"));
306: this .addElement(xpath, itemElement);
307: }
308: } catch (Exception e) {
309: log.error(e.getMessage(), e);
310: }
311: }
312:
313: /**
314: * Method for meta data.
315: * @todo use QTIConstantStrings
316: * @param fieldlabel field label
317: *
318: * @return value
319: */
320: public String getFieldentry(String fieldlabel) {
321: if (log.isDebugEnabled()) {
322: log.debug("getFieldentry(String " + fieldlabel + ")");
323: }
324:
325: String xpath = "section/qtimetadata/qtimetadatafield/fieldlabel[text()='"
326: + fieldlabel + "']/following-sibling::fieldentry";
327:
328: return super .getFieldentry(xpath);
329: }
330:
331: /**
332: * Method for meta data.
333: *
334: * @param fieldlabel label
335: * @param setValue value
336: */
337: public void setFieldentry(String fieldlabel, String setValue) {
338: if (log.isDebugEnabled()) {
339: log.debug("setFieldentry(String " + fieldlabel
340: + ", String " + setValue + ")");
341: }
342:
343: String xpath = "section/qtimetadata/qtimetadatafield/fieldlabel[text()='"
344: + fieldlabel + "']/following-sibling::fieldentry";
345: super .setFieldentry(xpath, setValue);
346: }
347:
348: /**
349: * Method for meta data.
350: *
351: * @param fieldlabel to be added
352: */
353: public void createFieldentry(String fieldlabel) {
354: if (log.isDebugEnabled()) {
355: log.debug("createFieldentry(String " + fieldlabel + ")");
356: }
357:
358: String xpath = "section/qtimetadata";
359: super .createFieldentry(xpath, fieldlabel);
360: }
361:
362: /**
363: * ASI OKI implementation
364: *
365: * @param itemId item id
366: */
367: public void addItemRef(String itemId) {
368: if (log.isDebugEnabled()) {
369: log.debug("addItem(String " + itemId + ")");
370: }
371:
372: try {
373: String xpath = basePath;
374: DocumentBuilderFactory dbf = DocumentBuilderFactory
375: .newInstance();
376: DocumentBuilder db = dbf.newDocumentBuilder();
377: Document document = db.newDocument();
378: Element element = document
379: .createElement(QTIConstantStrings.ITEMREF);
380: element.setAttribute(QTIConstantStrings.LINKREFID, itemId);
381: this .addElement(xpath, element);
382: } catch (ParserConfigurationException pce) {
383: log.error("Exception thrown from addItemRef() : "
384: + pce.getMessage());
385: pce.printStackTrace();
386: }
387: }
388:
389: /**
390: * remove item ref
391: *
392: * @param itemId igem id
393: */
394: public void removeItemRef(String itemId) {
395: if (log.isDebugEnabled()) {
396: log.debug("removeItem(String " + itemId + ")");
397: }
398:
399: String xpath = basePath + "/" + QTIConstantStrings.ITEMREF
400: + "[@" + QTIConstantStrings.LINKREFID + "=\"" + itemId
401: + "\"]";
402: this .removeElement(xpath);
403: }
404:
405: /**
406: * add section ref
407: *
408: * @param sectionId section id
409: */
410: public void addSectionRef(String sectionId) {
411: if (log.isDebugEnabled()) {
412: log.debug("addSection(String " + sectionId + ")");
413: }
414: try {
415: String xpath = basePath;
416: DocumentBuilderFactory dbf = DocumentBuilderFactory
417: .newInstance();
418: DocumentBuilder db = dbf.newDocumentBuilder();
419: Document document = db.newDocument();
420: Element element = document
421: .createElement(QTIConstantStrings.SECTIONREF);
422: element.setAttribute(QTIConstantStrings.LINKREFID,
423: sectionId);
424: this .addElement(xpath, element);
425: } catch (ParserConfigurationException pce) {
426: log.error("Exception thrown from addSectionRef() : "
427: + pce.getMessage());
428: pce.printStackTrace();
429: }
430: }
431:
432: /**
433: * remove section ref
434: *
435: * @param sectionId DOCUMENTATION PENDING
436: */
437: public void removeSectionRef(String sectionId) {
438: if (log.isDebugEnabled()) {
439: log.debug("removeSection(String " + sectionId + ")");
440: }
441:
442: String xpath = basePath + "/" + QTIConstantStrings.SECTIONREF
443: + "[@" + QTIConstantStrings.LINKREFID + "=" + sectionId
444: + "]";
445: this .removeElement(xpath);
446: }
447:
448: /**
449: * Order item refs
450: *
451: * @param itemRefIds list of ref ids
452: */
453: public void orderItemRefs(ArrayList itemRefIds) {
454: if (log.isDebugEnabled()) {
455: log.debug("orderItemRefs(ArrayList " + itemRefIds + ")");
456: }
457:
458: this .removeItemRefs();
459: int size = itemRefIds.size();
460: for (int i = 0; i < size; i++) {
461: this .addItemRef((String) itemRefIds.get(i));
462: }
463: }
464:
465: /**
466: * remove item refs
467: */
468: private void removeItemRefs() {
469: log.debug("removeItems()");
470:
471: String xpath = basePath + "/" + QTIConstantStrings.ITEMREF;
472: this .removeElement(xpath);
473: }
474:
475: /**
476: * get section refs
477: *
478: * @return list of section refs
479: */
480: public List getSectionRefs() {
481: log.debug("getSectionRefs()");
482: String xpath = basePath + "/" + QTIConstantStrings.SECTIONREF;
483:
484: return this .selectNodes(xpath);
485: }
486:
487: /**
488: * get section ref ids
489: *
490: * @return list of section ref ids
491: */
492: public List getSectionRefIds() {
493: List refs = this .getSectionRefs();
494: List ids = new ArrayList();
495: int size = refs.size();
496: for (int i = 0; i < size; i++) {
497: Element ref = (Element) refs.get(0);
498: String idString = ref
499: .getAttribute(QTIConstantStrings.LINKREFID);
500: ids.add(idString);
501: }
502:
503: return ids;
504: }
505:
506: /**
507: * get Xpath of section
508: * @return the Xpath
509: */
510: public String getBasePath() {
511: return basePath;
512: }
513:
514: /**
515: * set XPath of section
516: * @param basePath
517: */
518: public void setBasePath(String basePath) {
519: this .basePath = basePath;
520: }
521:
522: private String getAttachment(SectionDataIfc section) {
523: Set attachmentSet = (Set) section.getSectionAttachmentSet();
524: if (attachmentSet != null && attachmentSet.size() != 0) {
525: Iterator iter = attachmentSet.iterator();
526: AttachmentData attachmentData = null;
527: StringBuffer attachment = new StringBuffer();
528: while (iter.hasNext()) {
529: attachmentData = (AttachmentData) iter.next();
530: attachment.append(attachmentData.getResourceId()
531: .replaceAll(" ", ""));
532: attachment.append("|");
533: attachment.append(attachmentData.getFilename());
534: attachment.append("|");
535: attachment.append(attachmentData.getMimeType());
536: attachment.append("\n");
537: }
538: return attachment.toString();
539: } else {
540: return null;
541: }
542: }
543: }
|