de.thorstenberger.taskmodel.complex.complextaskhandling.subtasklets.impl.SubTasklet_MCBuilder.java Source code

Java tutorial

Introduction

Here is the source code for de.thorstenberger.taskmodel.complex.complextaskhandling.subtasklets.impl.SubTasklet_MCBuilder.java

Source

/*
    
Copyright (C) 2006 Thorsten Berger
    
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/**
 *
 */
package de.thorstenberger.taskmodel.complex.complextaskhandling.subtasklets.impl;

import static org.apache.commons.lang.ArrayUtils.addAll;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import de.thorstenberger.taskmodel.complex.RandomUtil;
import de.thorstenberger.taskmodel.complex.complextaskhandling.subtasklets.SubTasklet_MC;
import de.thorstenberger.taskmodel.complex.jaxb.ComplexTaskHandling;
import de.thorstenberger.taskmodel.complex.jaxb.ComplexTaskHandling.Try.Page.McSubTask;
import de.thorstenberger.taskmodel.complex.jaxb.McSubTaskDef;
import de.thorstenberger.taskmodel.complex.jaxb.McSubTaskDef.Correct;
import de.thorstenberger.taskmodel.complex.jaxb.McSubTaskDef.Incorrect;
import de.thorstenberger.taskmodel.complex.jaxb.ObjectFactory;
import de.thorstenberger.taskmodel.util.ReflectionHelper;

/**
 * @author Thorsten Berger
 *
 */
public class SubTasklet_MCBuilder {

    Log log = LogFactory.getLog(SubTasklet_MCBuilder.class);
    private ObjectFactory taskHandlingobjectFactory = new ObjectFactory();
    private RandomUtil r;

    public SubTasklet_MCBuilder(RandomUtil r) {
        this.r = r;
    }

    /**
     * Mgliche Antworten zu MC-Aufgaben hinzufgen.
     * @param newMcSubTask
     * @param mcSubTaskDef
     */
    void constructAnswersForMCSubTask(ComplexTaskHandling.Try.Page.McSubTask newMcSubTask,
            McSubTaskDef mcSubTaskDef) throws JAXBException {

        List allAvailableAnswers = mcSubTaskDef.getCorrectOrIncorrect();
        int[] availCorrectAnswersIndices = getIndices(allAvailableAnswers, Correct.class);
        int[] availIncorrectAnswersIndices = getIndices(allAvailableAnswers, Incorrect.class);

        // make sure the task definition is correct, i.e. that there are answers at all!
        if (availCorrectAnswersIndices.length == 0 || mcSubTaskDef.getDisplayedAnswers() == 0) {
            log.warn("Aufgabe " + mcSubTaskDef.getId() + " wird keine Antworten enthalten!");
            return;
        }
        // how many correct answers will be presented?
        int numOfCorrectAnswersToShow = getChosenNumCorrectAnswers(mcSubTaskDef, availCorrectAnswersIndices.length,
                availIncorrectAnswersIndices.length);

        // Anzahl der insg. anzuzeigenden Antworten
        int numOfAnswers = Math.min(numOfCorrectAnswersToShow + availIncorrectAnswersIndices.length,
                mcSubTaskDef.getDisplayedAnswers());
        int numOfIncorrectAnswersToShow = numOfAnswers - numOfCorrectAnswersToShow;

        // random permutation of indices to all available correct answer definitions
        int[] correctAnswersOrder = r.getPermutation(availCorrectAnswersIndices.length);
        // random permutation of indices to all available incorrect answer definitions
        int[] incorrectAnswersOrder = r.getPermutation(availIncorrectAnswersIndices.length);

        // final array of the indices of the answer definition that will be chosen
        int[] choosenAnswerIndices = addAll(
                selectFirst(availCorrectAnswersIndices, correctAnswersOrder, numOfCorrectAnswersToShow),
                selectFirst(availIncorrectAnswersIndices, incorrectAnswersOrder, numOfIncorrectAnswersToShow));

        if (mcSubTaskDef.isPreserveOrderOfAnswers()) {
            Arrays.sort(choosenAnswerIndices);
        } else {
            // use unique random generator to shuffle the selected answers
            // This means everybody may get the same answers, but not necessarily in the same order
            choosenAnswerIndices = new RandomUtil().shuffle(choosenAnswerIndices);
        }

        // Array der ausgewhlten Antworten
        ComplexTaskHandling.Try.Page.McSubTask.Answer[] toInsert = new ComplexTaskHandling.Try.Page.McSubTask.Answer[numOfAnswers];

        int insertIndex = 0;
        // insert correct answers to represent
        for (int i = 0; i < numOfAnswers; i++) {
            toInsert[insertIndex++] = createAnswerType(allAvailableAnswers.get(choosenAnswerIndices[i]));
        }

        // und jetzt die Antworten in XML entspr. einfgen
        List answers = newMcSubTask.getAnswer();
        for (int i = 0; i < toInsert.length; i++) {
            answers.add(toInsert[i]);
        }

    }

    /**
     * Return the first <code>numToChoose</code> values from <code>values</code> as indexed by <code>indices</code><br/>
     * Example: selectFirst({10,20,30,40},{2,0,1,3},2) ==> {30,10}
     *
     * @param values
     * @param indices
     * @param numToChoose
     * @return
     */
    private int[] selectFirst(int[] values, int[] indices, int numToChoose) {
        if (values.length != indices.length || indices.length < numToChoose)
            throw new IllegalArgumentException(String.format(
                    "Internal implementation error, can't choose the first %d values of %s indexed by %s!",
                    numToChoose, ArrayUtils.toString(values), ArrayUtils.toString(indices)));

        int[] result = new int[numToChoose];
        for (int i = 0; i < numToChoose; i++) {
            result[i] = values[indices[i]];
        }
        return result;
    }

    /**
     * How many correct answers will be chosen for representation?
     *
     * @param mcSubTaskDef
     * @param availCorrectAnswers
     *            number of existing correct answer definitions
     * @param availIncorrectAnswers
     *            number of existing incorrect answer definitions
     * @return
     */
    protected int getChosenNumCorrectAnswers(McSubTaskDef mcSubTaskDef, int availCorrectAnswers,
            int availIncorrectAnswers) {
        int numOfCorrectAnswers;
        // nach Kategorie die Anzahl der richtigen Antworten festlegen
        if (mcSubTaskDef.getCategory().equals(SubTasklet_MC.CAT_SINGLESELECT)) {
            numOfCorrectAnswers = 1;
        } else {

            int wantedMinCorrect = mcSubTaskDef.isSetMinCorrectAnswers() ? mcSubTaskDef.getMinCorrectAnswers() : 1;

            // mindestens so viele richtige Antworten
            // das kommt daher, dass zu wenig falsche Fragen da sein knnten, um die gewnschte
            // Anzahl angezeigter Antworten zu erzeugen
            int minCorr = Math.max(mcSubTaskDef.getDisplayedAnswers() - availIncorrectAnswers, wantedMinCorrect);
            // und sicherheitshalber beschrnken
            minCorr = Math.min(minCorr, availCorrectAnswers);
            minCorr = Math.min(minCorr, mcSubTaskDef.getDisplayedAnswers());

            // maximal so viele richtige Antworten
            int maxCorr = Math.min(availCorrectAnswers, mcSubTaskDef.getDisplayedAnswers());
            if (mcSubTaskDef.isSetMaxCorrectAnswers()) {
                maxCorr = Math.min(maxCorr, mcSubTaskDef.getMaxCorrectAnswers());
            }

            // TODO Algorithmus verifizieren
            if (maxCorr < minCorr) {
                maxCorr = minCorr;
            }

            // ok, jetzt zufllig mit den berechneten Grenzen festlegen
            numOfCorrectAnswers = r.getInt(maxCorr - minCorr + 1) + minCorr;

        }
        return numOfCorrectAnswers;
    }

    void constructPreviewAnswersForMCSubTask(ComplexTaskHandling.Try.Page.McSubTask newMcSubTask,
            McSubTaskDef mcSubTaskDef) throws JAXBException {

        List<Correct> correctAnswers = SubTasklet_MCBuilder.filterList(mcSubTaskDef.getCorrectOrIncorrect(),
                Correct.class);
        List<Incorrect> incorrectAnswers = SubTasklet_MCBuilder.filterList(mcSubTaskDef.getCorrectOrIncorrect(),
                Incorrect.class);

        // sicherheitshalber prfen, drfte aber schon durch XML-Schema ausgeschlossen sein
        if (correctAnswers.size() == 0 || mcSubTaskDef.getDisplayedAnswers() == 0) {
            log.warn("Aufgabe " + mcSubTaskDef.getId() + " enthlt keine Antworten");
            return;
        }

        List answers = newMcSubTask.getAnswer();
        for (Correct ct : correctAnswers) {
            answers.add(createAnswerType(ct));
        }
        for (Incorrect ict : incorrectAnswers) {
            answers.add(createAnswerType(ict));
        }

    }

    /**
     * neuen AnswerType aus korrekter Antwort erstellen
     * @param correct
     * @return
     */
    private McSubTask.Answer createAnswerType(Object answer) throws JAXBException {

        ComplexTaskHandling.Try.Page.McSubTask.Answer ret;

        ret = taskHandlingobjectFactory.createComplexTaskHandlingTryPageMcSubTaskAnswer();

        ret.setRefId((String) ReflectionHelper.callMethod(answer, "getId", new Object[0]));
        ret.setSelected(false);

        return ret;

    }

    /**
     * Return all instances of <code>clazz</code> from the given list.
     *
     * @param correctOrIncorrect
     * @param clazz
     * @return
     */
    static List filterList(List correctOrIncorrect, Class<?> clazz) {
        List result = new ArrayList();
        for (Object o : correctOrIncorrect) {
            if (clazz.isInstance(o)) {
                result.add(o);
            }
        }
        return result;
    }

    /**
     * Return all instances of <code>clazz</code> from the given list.
     *
     * @param correctOrIncorrect
     * @param clazz
     * @return
     */
    static int[] getIndices(List correctOrIncorrect, Class<?> clazz) {
        List<Integer> selected = new ArrayList<Integer>();
        int idx = 0;
        for (Object o : correctOrIncorrect) {
            if (clazz.isInstance(o)) {
                selected.add(idx);
            }
            idx++;
        }
        return ArrayUtils.toPrimitive(selected.toArray(new Integer[selected.size()]));
    }

}