001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/sam/trunk/component/src/java/org/sakaiproject/tool/assessment/qti/asi/ASIBaseClass.java $
003: * $Id: ASIBaseClass.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.io.IOException;
023: import java.util.ArrayList;
024: import java.util.List;
025: import java.util.Random;
026:
027: import javax.xml.parsers.DocumentBuilder;
028: import javax.xml.parsers.DocumentBuilderFactory;
029: import javax.xml.parsers.ParserConfigurationException;
030:
031: import org.apache.commons.lang.StringEscapeUtils;
032: import org.apache.commons.logging.Log;
033: import org.apache.commons.logging.LogFactory;
034: import org.w3c.dom.CharacterData;
035: import org.w3c.dom.Comment;
036: import org.w3c.dom.DOMException;
037: import org.w3c.dom.Document;
038: import org.w3c.dom.Element;
039: import org.w3c.dom.Node;
040: import org.w3c.dom.NodeList;
041: import org.w3c.dom.Text;
042: import org.xml.sax.SAXException;
043:
044: import org.sakaiproject.tool.assessment.qti.constants.QTIConstantStrings;
045: import org.sakaiproject.tool.assessment.qti.util.XmlStringBuffer;
046:
047: /**
048: * <p>Copyright: Copyright (c) 2004</p>
049: * <p>Organization: Sakai Project</p>
050: * @author rshastri
051: * @author Ed Smiley esmiley@stanford.edu
052: * @version $Id: ASIBaseClass.java 9274 2006-05-10 22:50:48Z daisyf@stanford.edu $
053: */
054: public abstract class ASIBaseClass extends XmlStringBuffer {
055: private static Log log = LogFactory.getLog(ASIBaseClass.class);
056: private static final long serialVersionUID = 5670937321581940933L;
057: private String idString;
058:
059: /**
060: * Creates a new ASIBaseClass object.
061: */
062: protected ASIBaseClass() {
063: super ();
064: }
065:
066: /**
067: * Creates a new ASIBaseClass object.
068: *
069: * @param xml XML string
070: */
071: protected ASIBaseClass(String xml) {
072: super (xml);
073: }
074:
075: /**
076: * Creates a new ASIBaseClass object.
077: *
078: * @param document Document
079: */
080: protected ASIBaseClass(Document document) {
081: super (document);
082: }
083:
084: /**
085: * extract string for tag
086: * @param tagName name of tag
087: * @return a String
088: * @throws ParserConfigurationException
089: * @throws SAXException
090: * @throws IOException
091: * @throws DOMException
092: */
093: protected String extractString(String tagName)
094: throws ParserConfigurationException, SAXException,
095: IOException, DOMException {
096: if (log.isDebugEnabled()) {
097: log.debug("extractString(String " + tagName + ")");
098: }
099:
100: String title = null;
101: String description = null;
102: NodeList nodes = this .getDocument().getElementsByTagName(
103: tagName);
104: Element element = (Element) nodes.item(0);
105: title = element.getAttribute(QTIConstantStrings.TITLE);
106: description = title;
107:
108: int size = nodes.getLength();
109: for (int i = 0; i < size; i++) {
110: Element node = (Element) nodes.item(i);
111: node.setAttribute(QTIConstantStrings.IDENT, this
112: .getIdString());
113: }
114:
115: return this .stringValue();
116: }
117:
118: /**
119: * Simple wrapper over commons lang util method, but we may add additional
120: * logic in the future for special QTI export purposes.
121: * @param s
122: * @return escaped string e.g.
123: * < \u04D0rnesen & Jones > becomes <Ӑrnesen & Jones >
124: */
125: public static String escapeXml(String s) {
126: if (s == null)
127: return "";
128: return StringEscapeUtils.escapeXml(s);
129: }
130:
131: /**
132: * DOCUMENTATION PENDING
133: *
134: * @param xpath DOCUMENTATION PENDING
135: *
136: * @return DOCUMENTATION PENDING
137: */
138: protected String getFieldentry(String xpath) {
139: if (log.isDebugEnabled()) {
140: log.debug("getFieldentry(String " + xpath + ")");
141: }
142:
143: String val = null;
144: int no = 0;
145:
146: List metadataList;
147: try {
148: metadataList = this .selectNodes(xpath);
149: no = metadataList.size();
150:
151: if (metadataList.size() > 0) {
152: Document document = this .getDocument();
153: Element fieldentry = (Element) metadataList.get(0);
154: CharacterData fieldentryText = (CharacterData) fieldentry
155: .getFirstChild();
156:
157: Integer getTime = null;
158: if ((fieldentryText != null)
159: && (fieldentryText.getNodeValue() != null)
160: && (fieldentryText.getNodeValue().trim()
161: .length() > 0)) {
162: val = fieldentryText.getNodeValue();
163: }
164: }
165: } catch (DOMException ex) {
166: log.error(ex.getMessage(), ex);
167: }
168:
169: catch (Exception ex) {
170: log.error(ex.getMessage(), ex);
171: }
172:
173: return val;
174: }
175:
176: /**
177: * Set field entry.
178: *
179: * @param xpath
180: * @param setValue
181: */
182: protected void setFieldentry(String xpath, String value) {
183: setFieldentry(xpath, value, false);
184: }
185:
186: /**
187: * Set field entry.
188: *
189: * @param xpath
190: * @param setValue
191: * @param noEscapeXML
192: */
193: protected void setFieldentry(String xpath, String value,
194: boolean noEscapeXML) {
195: String setValue = null;
196:
197: if (noEscapeXML) {
198: setValue = value;
199: } else {
200: setValue = escapeXml(value);
201: }
202:
203: if (log.isDebugEnabled()) {
204: log.debug("setFieldentry(String " + xpath + ", String "
205: + setValue + ")");
206: }
207:
208: List metadataList;
209: try {
210: metadataList = this .selectNodes(xpath);
211: int no = metadataList.size();
212: String val = null;
213:
214: if (metadataList.size() > 0) {
215: Document document = this .getDocument();
216: Element fieldentry = (Element) metadataList.get(0);
217: CharacterData fieldentryText = (CharacterData) fieldentry
218: .getFirstChild();
219:
220: Integer getTime = null;
221: if ((fieldentryText != null)
222: && (fieldentryText.getNodeValue() != null)
223: && (fieldentryText.getNodeValue().trim()
224: .length() > 0)) {
225: val = fieldentryText.getNodeValue();
226: }
227:
228: if (setValue != null) {
229: if (fieldentryText == null) {
230: Text newElementText = fieldentry
231: .getOwnerDocument().createTextNode(
232: setValue);
233:
234: fieldentry.appendChild(newElementText);
235: fieldentryText = (CharacterData) fieldentry
236: .getFirstChild();
237: } else {
238: fieldentryText.setNodeValue(setValue);
239: }
240: }
241: }
242: } catch (ParserConfigurationException e) {
243: log.error(e.getMessage(), e);
244: } catch (SAXException e) {
245: log.error(e.getMessage(), e);
246: } catch (IOException e) {
247: log.error(e.getMessage(), e);
248: }
249: }
250:
251: /**
252: *
253: *
254: * @param xpath
255: * @param fieldlabel
256: */
257: protected void createFieldentry(String xpath, String fieldlabel) {
258: if (log.isDebugEnabled()) {
259: log.debug("createFieldentry(String " + xpath + ", String "
260: + fieldlabel + ")");
261: }
262:
263: try {
264: List qtimetadataNodes = this .selectNodes(xpath);
265: if (qtimetadataNodes.size() > 0) {
266: Node qtimetadataNode = (Node) qtimetadataNodes.get(0);
267: DocumentBuilderFactory dbf = DocumentBuilderFactory
268: .newInstance();
269: DocumentBuilder db = dbf.newDocumentBuilder();
270: Document newDocument = db.newDocument();
271:
272: Element qtimetadataField = newDocument
273: .createElement(QTIConstantStrings.QTIMETADATAFIELD);
274: Element fieldlabelElement = newDocument
275: .createElement(QTIConstantStrings.FIELDLABEL);
276: Element fieldentryElement = newDocument
277: .createElement(QTIConstantStrings.FIELDENTRY);
278:
279: Text fieldlabelText = newDocument
280: .createTextNode(QTIConstantStrings.FIELDLABEL);
281: fieldlabelText.setNodeValue(fieldlabel);
282: fieldlabelElement.appendChild(fieldlabelText);
283:
284: Text fieldentryText = newDocument
285: .createTextNode(QTIConstantStrings.FIELDENTRY);
286: fieldentryElement.appendChild(fieldentryText);
287:
288: Node importedFLE = qtimetadataField.getOwnerDocument()
289: .importNode(fieldlabelElement, true);
290: Node importedFEE = qtimetadataField.getOwnerDocument()
291: .importNode(fieldentryElement, true);
292: qtimetadataField.appendChild(importedFLE);
293: qtimetadataField.appendChild(importedFEE);
294: Node importedField = qtimetadataNode.getOwnerDocument()
295: .importNode(qtimetadataField, true);
296: qtimetadataNode.appendChild(importedField);
297: }
298: } catch (ParserConfigurationException pce) {
299: log.error("Exception thrown from createFieldentry()"
300: + pce.getMessage(), pce);
301: pce.printStackTrace();
302: } catch (Exception ex) {
303: log.error(ex.getMessage(), ex);
304: }
305: }
306:
307: /**
308: * Methods shared by Assessment and Section only
309: *
310: * @param basePath
311: *
312: * @return
313: */
314: /**
315: *
316: *
317: * @param basePath
318: *
319: * @return
320: */
321: protected List getAllSections(String basePath) {
322: if (log.isDebugEnabled()) {
323: log.debug("getAllSections(String " + basePath + ")");
324: }
325:
326: String xpath = basePath + "/" + QTIConstantStrings.SECTION;
327: List nodes = this .selectNodes(xpath);
328: List clonedList = new ArrayList();
329: int size = nodes.size();
330: for (int i = 0; i < size; i++) {
331: Node clonedNode = ((Node) nodes.get(i)).cloneNode(true);
332: clonedList.add(clonedNode);
333: }
334:
335: return clonedList;
336: }
337:
338: /**
339: *
340: *
341: * @param basePath
342: */
343: protected void removeSections(String basePath) {
344: if (log.isDebugEnabled()) {
345: log.debug("removeSections(String " + basePath + ")");
346: }
347:
348: String xpath = basePath + "/" + QTIConstantStrings.SECTION;
349: this .removeElement(xpath);
350: }
351:
352: /**
353: *
354: *
355: * @param basePath
356: *
357: * @return
358: */
359: protected ArrayList selectSections(String basePath) {
360: if (log.isDebugEnabled()) {
361: log.debug("selectSections(String " + basePath + ")");
362: log.debug("After Remove Section: " + this .stringValue());
363: }
364:
365: ArrayList sections = new ArrayList();
366: try {
367: String xpath = basePath + "/"
368: + QTIConstantStrings.SELECTION_ORDERING + "/";
369: String selectionXPath = xpath
370: + QTIConstantStrings.SELECTION;
371:
372: List selectNodes = this .selectNodes(selectionXPath);
373:
374: int selectNodeSize = selectNodes.size();
375: for (int i = 0; i < selectNodeSize; i++) {
376: Element selectElement = (Element) selectNodes.get(i);
377: sections.addAll(processSelectElement(basePath,
378: selectElement));
379: }
380:
381: if (selectNodeSize == 0) {
382: // no select element, then select all items
383: sections.addAll(this .getAllSections(basePath));
384: }
385: } catch (Exception ex) {
386: log.error(ex.getMessage(), ex);
387: }
388:
389: removeSections(basePath);
390:
391: return sections;
392: }
393:
394: /**
395: *
396: *
397: * @param basePath
398: * @param selectElement
399: *
400: * @return
401: */
402: protected List processSelectElement(String basePath,
403: Element selectElement) {
404: if (log.isDebugEnabled()) {
405: log.debug("processSelectElement(String " + basePath
406: + ", Element" + selectElement + ")");
407: }
408:
409: int selectNumber = -1;
410: String sourceBankId = null;
411:
412: // there is no select number and sourceBank_ref
413: // then select all items within this section.
414: if ((selectNumber == -1) && (sourceBankId == null)) {
415: return getAllSections(basePath);
416: }
417:
418: // there is select number but no sourceBank_ref
419: // then select number of items within this section.
420: if ((selectNumber > 0) && (sourceBankId == null)) {
421: return getNumOfSections(basePath, selectNumber);
422: }
423:
424: // We are not supporting object bank for sections at this time.
425: return null;
426: }
427:
428: /**
429: *
430: *
431: * @param basePath
432: * @param selectNumber
433: *
434: * @return
435: */
436: protected List getNumOfSections(String basePath, int selectNumber) {
437: if (log.isDebugEnabled()) {
438: log.debug("getNumOfSections(String " + basePath + ", int "
439: + selectNumber + ")");
440: }
441:
442: List list = new ArrayList();
443: List clonedList = new ArrayList();
444: List allSections = getAllSections(basePath);
445: long seed = System.currentTimeMillis();
446: int allItemSize = allSections.size();
447: Random random = new Random(seed);
448: while (list.size() < selectNumber) {
449: int randomNum = random.nextInt(allItemSize);
450: Object item = allSections.get(randomNum);
451: if (!list.contains(item)) {
452: list.add(item);
453: }
454: }
455:
456: for (int i = 0; i < selectNumber; i++) {
457: Node clonedNode = ((Node) list.get(i)).cloneNode(true);
458: clonedList.add(clonedNode);
459: }
460:
461: return clonedList;
462: }
463:
464: // /**
465: // *
466: // *
467: // * @param basePath
468: // * @param sections
469: // */
470: // protected void orderSections(String basePath, ArrayList sections, int qtiVersion)
471: // {
472: // if(log.isDebugEnabled())
473: // {
474: // log.debug(
475: // "orderSections(String " + basePath + ", ArrayList " + sections + ")");
476: // }
477: //
478: // try
479: // {
480: // String xpath =
481: // basePath + "/" + QTIConstantStrings.SELECTION_ORDERING + "/";
482: // String orderingXPath = xpath + QTIConstantStrings.ORDER;
483: // List orderNodes = this.selectNodes(orderingXPath);
484: // if((orderNodes != null) && (orderNodes.size() > 0))
485: // {
486: // Element order = (Element) orderNodes.get(0);
487: // String orderType = order.getAttribute(QTIConstantStrings.ORDER_TYPE);
488: // if("Random".equalsIgnoreCase(orderType))
489: // {
490: // //Randomly order items.
491: // long seed = System.currentTimeMillis();
492: // Random rand = new Random(seed);
493: // int size = sections.size();
494: // for(int i = 0; i < size; i++)
495: // {
496: // int randomNum = rand.nextInt(size);
497: // Object temp = sections.get(i);
498: // sections.set(i, sections.get(randomNum));
499: // sections.set(randomNum, temp);
500: // }
501: // }
502: // }
503: // }
504: // catch(Exception ex)
505: // {
506: // log.error(ex.getMessage(), ex);
507: // }
508: //
509: // addSections(basePath, sections, qtiVersion);
510: // }
511:
512: // /**
513: // *
514: // *
515: // * @param basePath
516: // * @param sections
517: // */
518: // protected void addSections(String basePath, ArrayList sections, int qtiVersion)
519: // {
520: // if(log.isDebugEnabled())
521: // {
522: // log.debug(
523: // "addSections(String " + basePath + ", ArrayList " + sections + ")");
524: // }
525: //
526: // try
527: // {
528: // String xpath = basePath;
529: // for(int i = 0; i < sections.size(); i++)
530: // {
531: // Element sectionElement = (Element) sections.get(i);
532: // Document sectionDoc = XmlUtil.createDocument();
533: // Node newNode = sectionDoc.importNode(sectionElement, true);
534: // sectionDoc.appendChild(newNode);
535: // Section section = new Section(sectionDoc, qtiVersion);
536: //
537: // //Shuffle Section if specified within Item.
538: // section.selectAndOrder();
539: // sectionElement = (Element) section.getDocument().getFirstChild();
540: //
541: // this.addElement(xpath, sectionElement);
542: // }
543: // }
544: // catch(ParserConfigurationException e)
545: // {
546: // log.error(e.getMessage(), e);
547: // }
548: // catch(SAXException e)
549: // {
550: // log.error(e.getMessage(), e);
551: // }
552: // catch(IOException e)
553: // {
554: // log.error(e.getMessage(), e);
555: // }
556: // }
557:
558: protected void wrappingMattext() {
559: log.debug("wrappingMattext()");
560:
561: try {
562: NodeList list = this .getDocument().getElementsByTagName(
563: QTIConstantStrings.MATTEXT);
564: int size = list.getLength();
565: for (int i = 0; i < size; i++) {
566: Node node = list.item(i);
567: Node childNode = node.getFirstChild();
568: if ((childNode != null)
569: && childNode instanceof CharacterData) {
570: CharacterData cdi = (CharacterData) childNode;
571: String data = cdi.getData();
572:
573: //modify this string;
574: DocumentBuilderFactory dbf = DocumentBuilderFactory
575: .newInstance();
576: DocumentBuilder db = dbf.newDocumentBuilder();
577: Document doc = db.newDocument();
578: Comment comment = doc.createComment(data);
579: node.appendChild(node.getOwnerDocument()
580: .importNode(comment, true));
581: cdi.setData("");
582: }
583: }
584: } catch (ParserConfigurationException e) {
585: log.error(e.getMessage(), e);
586: } catch (SAXException e) {
587: log.error(e.getMessage(), e);
588: } catch (IOException e) {
589: log.error(e.getMessage(), e);
590: }
591: }
592:
593: public String getIdString() {
594: return idString;
595: }
596:
597: public void setIdString(String idString) {
598: this.idString = idString;
599: }
600: }
|