/* * Question.java * * Created on 29 August 2006, 10:32 * */ package uk.co.bytemark.vm.enigma.inquisition.questions; import java.io.Serializable; import java.util.regex.Matcher; import java.util.regex.Pattern; import uk.co.bytemark.vm.enigma.inquisition.misc.Utils; /** * <tt>Question</tt> is an abstract base class for exam questions of various types. */ public abstract class AbstractQuestion implements Serializable, Question { private static final String COPY_FROM_QUESTION_TAG = "<CopyFromQuestion/>"; protected String questionText; protected String explanationText; private static final Pattern COPY_TO_EXPLANATION_PATTERN = Pattern.compile( "<CopyToExplanation>(.*?)</CopyToExplanation>", Pattern.DOTALL | Pattern.CASE_INSENSITIVE); public static String removeCopyToExplanationBlocks(String questionText) { Matcher matcher = COPY_TO_EXPLANATION_PATTERN.matcher(questionText); return matcher.replaceAll("$1"); } /** * Creates a new instance of Question * * @param questionText * HTML text to be used to pose this question. * @param explanationText * HTML text to be used to indicate and explain the correct answer to this question. */ protected AbstractQuestion(String questionText, String explanationText) { Utils.checkArgumentNotNull(questionText, "questionText"); Utils.checkArgumentNotNull(explanationText, "explanationText"); if (questionText == null || explanationText == null) throw new NullPointerException("Arguments must be non-null"); this.questionText = questionText; this.explanationText = explanationText; } protected AbstractQuestion() { // TODO: Get rid of this } /** * Returns the HTML text to be used to pose this question. */ public String getQuestionText() { return questionText; } public String getSubstitutedQuestionText() { return removeCopyToExplanationBlocks(questionText); } /** * Returns the HTML text to be used to indicate and explain the correct answer to this question. */ public String getExplanationText() { return explanationText; } /** * Give a name for questions of this type. Should return a short string that can preface "question", for example, * "Multiple choice". This is called by <tt>toString()</tt>. * * @return a short name for questions of this type. */ public abstract String getQuestionTypeName(); @Override public String toString() { return getQuestionTypeName() + " question: \"" + questionText.subSequence(0, Math.min(0 + 65, questionText.length())) + "...\""; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((explanationText == null) ? 0 : explanationText.hashCode()); result = prime * result + ((questionText == null) ? 0 : questionText.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final AbstractQuestion other = (AbstractQuestion) obj; if (explanationText == null) { if (other.explanationText != null) return false; } else if (!explanationText.equals(other.explanationText)) return false; if (questionText == null) { if (other.questionText != null) return false; } else if (!questionText.equals(other.questionText)) return false; return true; } /** * Returns the explanation text, with any <CopyFromQuestion> tags in the explanation replaced with the contents of * the <CopyToExplanation> tag in the question */ public String getSubstitutedExplanationText() { return substituteExplanationText(questionText, explanationText); } public static String substituteExplanationText(String questionText, String explanationText) { return explanationText.replace(COPY_FROM_QUESTION_TAG, getQuestionSectionToCopy(questionText)); } private static String getQuestionSectionToCopy(String questionText) { Matcher matcher = COPY_TO_EXPLANATION_PATTERN.matcher(questionText); String match = null; while (matcher.find()) { if (match == null) match = matcher.group(1); else return "ERROR: Multiple <CopyToExplanation> tags in question text"; // TODO: Check for this in constructor instead / as well } if (match == null) return "ERROR: No CopyToExplanation tag found"; else return match; } }