diff --git a/src/main/java/org/olat/core/util/filter/impl/NekoHTMLFilter.java b/src/main/java/org/olat/core/util/filter/impl/NekoHTMLFilter.java
index 3370d64a25046f25a0daf179dd51875754815287..b2f04a158f2c087eba62d1617def02f3ab2ea4d7 100644
--- a/src/main/java/org/olat/core/util/filter/impl/NekoHTMLFilter.java
+++ b/src/main/java/org/olat/core/util/filter/impl/NekoHTMLFilter.java
@@ -27,7 +27,8 @@ import java.util.HashSet;
 import java.util.Set;
 
 import org.cyberneko.html.parsers.SAXParser;
-import org.olat.core.logging.LogDelegator;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
 import org.olat.core.util.filter.Filter;
 import org.olat.core.util.io.LimitedContentWriter;
 import org.olat.search.service.document.file.FileDocumentFactory;
@@ -45,7 +46,9 @@ import org.xml.sax.helpers.DefaultHandler;
  * Initial Date:  2 dec. 2009 <br>
  * @author srosse
  */
-public class NekoHTMLFilter extends LogDelegator implements Filter {
+public class NekoHTMLFilter implements Filter {
+	
+	private static final OLog log = Tracing.createLoggerFor(NekoHTMLFilter.class);
 	
 	public static final Set<String> blockTags = new HashSet<String>();
 	static {
@@ -66,13 +69,13 @@ public class NekoHTMLFilter extends LogDelegator implements Filter {
 			parser.parse(new InputSource(new StringReader(original)));
 			return contentHandler.toString();
 		} catch (SAXException e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		} catch (IOException e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		} catch (Exception e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		}
 	}
@@ -86,13 +89,13 @@ public class NekoHTMLFilter extends LogDelegator implements Filter {
 			parser.parse(new InputSource(in));
 			return contentHandler.getContent();
 		} catch (SAXException e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		} catch (IOException e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		} catch (Exception e) {
-			logError("", e);
+			log.error("", e);
 			return null;
 		}
 	}
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Constants.java b/src/main/java/org/olat/ims/qti21/QTI21Constants.java
index 7fbbedb7819be9b95e7c7fbac20744dc2507a370..83fb9cbf76652a6cf4badb69fe846b97f81a1478 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Constants.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Constants.java
@@ -47,6 +47,12 @@ public class QTI21Constants {
 	
 	public static final ComplexReferenceIdentifier MAXSCORE_CLX_IDENTIFIER = ComplexReferenceIdentifier.parseString(MAXSCORE);
 	
+	public static final String MINSCORE = "MINSCORE";
+	
+	public static final Identifier MINSCORE_IDENTIFIER = Identifier.assumedLegal(MINSCORE);
+	
+	public static final ComplexReferenceIdentifier MINSCORE_CLX_IDENTIFIER = ComplexReferenceIdentifier.parseString(MINSCORE);
+	
 	public static final String PASS = "PASS";
 
 	public static final Identifier PASS_IDENTIFIER = Identifier.assumedLegal(PASS);
@@ -55,8 +61,20 @@ public class QTI21Constants {
 	
 	public static final Identifier FEEDBACKBASIC_IDENTIFIER = Identifier.parseString(FEEDBACKBASIC);
 	
-	public static final IdentifierValue CORRECT = new IdentifierValue("correct");
+	public static final String FEEDBACKMODAL = "FEEDBACKMODAL";
+	
+	public static final Identifier FEEDBACKMODAL_IDENTIFIER = Identifier.parseString(FEEDBACKMODAL);
+	
+	public static final String CORRECT = "correct";
+
+	public static final Identifier CORRECT_IDENTIFIER = Identifier.parseString(CORRECT);
+	
+	public static final IdentifierValue CORRECT_IDENTIFIER_VALUE = new IdentifierValue(CORRECT);
+	
+	public static final String INCORRECT = "incorrect";
+	
+	public static final Identifier INCORRECT_IDENTIFIER = Identifier.parseString(INCORRECT);
 	
-	public static final IdentifierValue INCORRECT = new IdentifierValue("incorrect");
+	public static final IdentifierValue INCORRECT_IDENTIFIER_VALUE = new IdentifierValue(INCORRECT);
 
 }
diff --git a/src/main/java/org/olat/ims/qti21/QTI21Service.java b/src/main/java/org/olat/ims/qti21/QTI21Service.java
index a3bbceb7aa32ace564d688d8723e3c62a7487121..05d33efa148bd21b4419fbd9d3525423290c7261 100644
--- a/src/main/java/org/olat/ims/qti21/QTI21Service.java
+++ b/src/main/java/org/olat/ims/qti21/QTI21Service.java
@@ -37,6 +37,7 @@ import org.olat.repository.RepositoryEntryRef;
 import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
 import uk.ac.ed.ph.jqtiplus.node.result.AssessmentResult;
 import uk.ac.ed.ph.jqtiplus.notification.NotificationRecorder;
+import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
 import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentObject;
 import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
 import uk.ac.ed.ph.jqtiplus.state.ItemSessionState;
@@ -59,6 +60,8 @@ public interface QTI21Service {
 	 */
 	public QtiSerializer qtiSerializer();
 	
+	public QtiXmlReader qtiXmlReader();
+	
 	/**
 	 * The manager for custom extensions to QTI (MathExtensio )
 	 * @return
diff --git a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
index 60ff6c6330e1e4fc6c60f12cc6d3cbb047c57456..8bf959fddfafe0610a8b2a3c1a9529569e6ace32 100644
--- a/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
+++ b/src/main/java/org/olat/ims/qti21/manager/QTI21ServiceImpl.java
@@ -179,6 +179,10 @@ public class QTI21ServiceImpl implements QTI21Service, InitializingBean, Disposa
         return new QtiSerializer(jqtiExtensionManager());
     }
     
+    @Override
+    public QtiXmlReader qtiXmlReader() {
+    	return new QtiXmlReader(jqtiExtensionManager());
+    }
     
 	
 	@SuppressWarnings("unchecked")
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java
new file mode 100644
index 0000000000000000000000000000000000000000..a385c2adef2b8862b0ebbf39363ceb89ef23ce70
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentBuilderHelper.java
@@ -0,0 +1,123 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.stream.StreamResult;
+
+import org.olat.core.gui.render.StringOutput;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.exception.QtiModelException;
+import uk.ac.ed.ph.jqtiplus.node.LoadingContext;
+import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+
+/**
+ * 
+ * Initial date: 09.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class AssessmentBuilderHelper {
+	
+	private static final OLog log = Tracing.createLoggerFor(AssessmentBuilderHelper.class);
+	
+	private final QtiSerializer qtiSerializer;
+	
+	public AssessmentBuilderHelper() {
+		JqtiExtensionManager jqtiExtensionManager = new JqtiExtensionManager();
+		qtiSerializer = new QtiSerializer(jqtiExtensionManager);
+	}
+	
+	public AssessmentBuilderHelper(QtiSerializer qtiSerializer) {
+		this.qtiSerializer = qtiSerializer;
+	}
+	
+	public String toString(List<FlowStatic> statics) {
+		StringOutput sb = new StringOutput();
+		if(statics != null && statics.size() > 0) {
+			for(FlowStatic flowStatic:statics) {
+				qtiSerializer.serializeJqtiObject(flowStatic, new StreamResult(sb));
+			}
+		}
+		return sb.toString();
+	}
+	
+	public List<Block> parseHtml(String html) {
+		//tinymce bad habits
+		if(html.startsWith("<p>&nbsp;")) {
+			html = html.replace("<p>&nbsp;", "<p>");
+		}
+		Document document = htmlToDOM("<html>" + html + "</html>");
+		LoadingContext context = new HTMLLoadingContext();
+		
+		ItemBody helper = new ItemBody(null);
+		helper.load(document.getDocumentElement(), context);	
+		return helper.getBlocks();
+	}
+	
+	/**
+	 * This method use the standard XML parser. It's not really
+	 * good but QTIWorks want DOM Level 2 elements.
+	 * 
+	 * @param content
+	 * @return
+	 */
+	private Document htmlToDOM(String content) {
+		try {
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			factory.setValidating(false);
+			factory.setNamespaceAware(true);
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			Document doc = builder.parse(new ByteArrayInputStream(content.getBytes()));
+			return doc;
+		} catch (ParserConfigurationException | SAXException | IOException e) {
+			log.error("", e);
+			return null;
+		}
+	}
+	
+	private static final class HTMLLoadingContext implements LoadingContext {
+
+		@Override
+		public JqtiExtensionManager getJqtiExtensionManager() {
+			return null;
+		}
+
+		@Override
+		public void modelBuildingError(QtiModelException exception, Node badNode) {
+			//
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..c206f27fd8028e174b9bd45957fe5a1abd245ce7
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilder.java
@@ -0,0 +1,274 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import static org.olat.ims.qti21.QTI21Constants.MAXSCORE_IDENTIFIER;
+import static org.olat.ims.qti21.QTI21Constants.MINSCORE_IDENTIFIER;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.olat.ims.qti21.QTI21Constants;
+
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.item.ModalFeedback;
+import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseCondition;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseProcessing;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseRule;
+import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration;
+import uk.ac.ed.ph.jqtiplus.node.shared.declaration.DefaultValue;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.value.FloatValue;
+import uk.ac.ed.ph.jqtiplus.value.Value;
+
+/**
+ * 
+ * 
+ * Initial date: 08.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class AssessmentItemBuilder {
+
+	protected final AssessmentItem assessmentItem;
+	protected final QtiSerializer qtiSerializer;
+	protected final AssessmentBuilderHelper builderHelper;
+	
+	private ScoreBuilder minScoreBuilder;
+	private ScoreBuilder maxScoreBuilder;
+	
+	private ModalFeedbackBuilder correctFeedback;
+	private ModalFeedbackBuilder incorrectFeedback;
+	private List<ModalFeedbackBuilder> additionalFeedbacks = new ArrayList<>();
+	
+	public AssessmentItemBuilder(AssessmentItem assessmentItem, QtiSerializer qtiSerializer) {
+		this.assessmentItem = assessmentItem;
+		this.qtiSerializer = qtiSerializer;
+		builderHelper = new AssessmentBuilderHelper(qtiSerializer);
+		extract();
+	}
+	
+	protected void extract() {
+		extractMinScore();
+		extractMaxScore();
+		extractModalFeedbacks();
+	}
+	
+	private void extractMinScore() {
+		OutcomeDeclaration outcomeDeclaration = assessmentItem.getOutcomeDeclaration(MINSCORE_IDENTIFIER);
+		if(outcomeDeclaration != null) {
+			DefaultValue defaultValue = outcomeDeclaration.getDefaultValue();
+			if(defaultValue != null) {
+				Value minScoreValue = defaultValue.evaluate();
+				if(minScoreValue instanceof FloatValue) {
+					Double minScore = new Double(((FloatValue)minScoreValue).doubleValue());
+					minScoreBuilder = new ScoreBuilder(minScore, outcomeDeclaration);
+				}
+			}
+		}
+	}
+	
+	private void extractMaxScore() {
+		OutcomeDeclaration outcomeDeclaration = assessmentItem.getOutcomeDeclaration(MAXSCORE_IDENTIFIER);
+		if(outcomeDeclaration != null) {
+			DefaultValue defaultValue = outcomeDeclaration.getDefaultValue();
+			if(defaultValue != null) {
+				Value maxScoreValue = defaultValue.evaluate();
+				if(maxScoreValue instanceof FloatValue) {
+					Double maxScore = new Double(((FloatValue)maxScoreValue).doubleValue());
+					maxScoreBuilder = new ScoreBuilder(maxScore, outcomeDeclaration);
+				}
+			}
+		}
+	}
+	
+	private void extractModalFeedbacks() {
+		List<ModalFeedback> feedbacks = assessmentItem.getModalFeedbacks();
+		for(ModalFeedback feedback:feedbacks) {
+			ModalFeedbackBuilder feedbackBuilder = new ModalFeedbackBuilder(assessmentItem, feedback);
+			if(feedbackBuilder.isCorrectRule()) {
+				correctFeedback = feedbackBuilder;
+			} else if(feedbackBuilder.isIncorrectRule()) {
+				incorrectFeedback = feedbackBuilder;
+			} else {
+				additionalFeedbacks.add(feedbackBuilder);
+			}
+		}
+	}
+	
+	public String getTitle() {
+		return assessmentItem.getTitle();
+	}
+	
+	public void setTitle(String title) {
+		assessmentItem.setTitle(title);
+	}
+	
+	public ScoreBuilder getMinScoreBuilder() {
+		return minScoreBuilder;
+	}
+	
+	public void setMinScore(Double minScore) {
+		if(minScoreBuilder == null) {
+			minScoreBuilder = new ScoreBuilder(minScore, null);
+		} else {
+			minScoreBuilder.setScore(minScore);
+		}
+	}
+
+	public ScoreBuilder getMaxScoreBuilder() {
+		return maxScoreBuilder;
+	}
+	
+	public void setMaxScore(Double maxScore) {
+		if(maxScoreBuilder == null) {
+			maxScoreBuilder = new ScoreBuilder(maxScore, null);
+		} else {
+			maxScoreBuilder.setScore(maxScore);
+		}
+	}
+	
+	public ModalFeedbackBuilder getCorrectFeedback() {
+		return correctFeedback;
+	}
+	
+	public ModalFeedbackBuilder createCorrectFeedback() {
+		correctFeedback = new ModalFeedbackBuilder(assessmentItem, null);
+		
+		
+		return correctFeedback;
+	}
+	
+	public ModalFeedbackBuilder getIncorrectFeedback() {
+		return incorrectFeedback;
+	}
+	
+	public AssessmentBuilderHelper getHelper() {
+		return builderHelper;
+	}
+
+	public final void build() {
+		List<OutcomeDeclaration> outcomeDeclarations = assessmentItem.getOutcomeDeclarations();
+		outcomeDeclarations.clear();
+		
+		ResponseProcessing responseProcessing = assessmentItem.getResponseProcessing();
+		List<ResponseRule> responseRules = responseProcessing.getResponseRules();
+		responseRules.clear();
+		
+		List<ResponseDeclaration> responseDeclarations = assessmentItem.getResponseDeclarations();
+		responseDeclarations.clear();
+		
+		buildResponseDeclaration();
+		buildItemBody();
+		buildModalFeedback(outcomeDeclarations, responseRules);
+		buildScores(outcomeDeclarations, responseRules);
+		buildMainScoreRule(responseRules);
+	}
+	
+	protected void buildResponseDeclaration() {
+		//
+	}
+	
+	protected void buildItemBody() {
+		//
+	}
+	
+	protected abstract void buildMainScoreRule(List<ResponseRule> responseRules);
+	
+	protected void buildModalFeedback(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) {
+		//add feedbackbasic and feedbackmodal outcomes
+		if(correctFeedback != null || incorrectFeedback != null || additionalFeedbacks.size() > 0) {
+			OutcomeDeclaration basicOutcomeDeclaration = AssessmentItemFactory
+					.createOutcomeDeclarationForFeedbackBasic(assessmentItem);
+			outcomeDeclarations.add(basicOutcomeDeclaration);	
+			
+			OutcomeDeclaration modalOutcomeDeclaration = AssessmentItemFactory
+					.createOutcomeDeclarationForFeedbackModal(assessmentItem);
+			outcomeDeclarations.add(modalOutcomeDeclaration);
+		}
+
+		//add modal
+		List<ModalFeedback> modalFeedbacks = assessmentItem.getModalFeedbacks();
+		modalFeedbacks.clear();
+		
+		if(correctFeedback != null) {
+			ModalFeedback correctModalFeedback = AssessmentItemFactory
+					.createModalFeedback(assessmentItem, correctFeedback.getIdentifier(),
+							correctFeedback.getTitle(), correctFeedback.getText());
+			modalFeedbacks.add(correctModalFeedback);
+			
+			ResponseCondition feedbackCondition = AssessmentItemFactory
+					.createModalFeedbackBasicRule(assessmentItem.getResponseProcessing(), correctFeedback.getIdentifier(), QTI21Constants.CORRECT);
+			responseRules.add(feedbackCondition);
+		}
+		
+		if(incorrectFeedback != null) {
+			ModalFeedback incorrectModalFeedback = AssessmentItemFactory
+					.createModalFeedback(assessmentItem, incorrectFeedback.getIdentifier(),
+							incorrectFeedback.getTitle(), incorrectFeedback.getText());
+			modalFeedbacks.add(incorrectModalFeedback);
+			
+			ResponseCondition feedbackCondition = AssessmentItemFactory
+					.createModalFeedbackBasicRule(assessmentItem.getResponseProcessing(), incorrectFeedback.getIdentifier(), QTI21Constants.INCORRECT);
+			responseRules.add(feedbackCondition);
+		}
+	}
+	
+	/**
+	 * Add outcome declaration for score, min. score and mx. score.
+	 * and the response rules which ensure that the score is between
+	 * the max. score and min. score.
+	 * 
+	 * @param outcomeDeclarations
+	 * @param responseRules
+	 */
+	protected void buildScores(List<OutcomeDeclaration> outcomeDeclarations, List<ResponseRule> responseRules) {
+		if((getMinScoreBuilder() != null && getMinScoreBuilder().getScore() != null)
+				|| (getMaxScoreBuilder() != null && getMaxScoreBuilder().getScore() != null)) {
+			
+			OutcomeDeclaration outcomeDeclaration = AssessmentItemFactory
+					.createOutcomeDeclarationForScore(assessmentItem);
+			outcomeDeclarations.add(outcomeDeclaration);	
+		}
+		
+		if(getMinScoreBuilder() != null && getMinScoreBuilder().getScore() != null) {
+			OutcomeDeclaration outcomeDeclaration = AssessmentItemFactory
+					.createOutcomeDeclarationForMinScore(assessmentItem, getMinScoreBuilder().getScore().doubleValue());
+			outcomeDeclarations.add(outcomeDeclaration);
+			
+			//ensure that the score is not smaller than min. score value
+			ResponseRule minScoreBoundResponseRule = AssessmentItemFactory
+					.createMinScoreBoundLimitRule(assessmentItem.getResponseProcessing());
+			responseRules.add(minScoreBoundResponseRule);
+		}
+		
+		if(getMaxScoreBuilder() != null && getMaxScoreBuilder().getScore() != null) {
+			OutcomeDeclaration outcomeDeclaration = AssessmentItemFactory
+					.createOutcomeDeclarationForMaxScore(assessmentItem, getMaxScoreBuilder().getScore().doubleValue());
+			outcomeDeclarations.add(outcomeDeclaration);
+			
+			//ensure that the score is not bigger than the max. score value
+			ResponseRule maxScoreBoundResponseRule = AssessmentItemFactory
+					.createMaxScoreBoundLimitRule(assessmentItem.getResponseProcessing());
+			responseRules.add(maxScoreBoundResponseRule);
+		}
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
index 345c0f323688949997c5712fa709b9495071bc50..8d3113436dd23b8204a5c88bdab40181a8196975 100644
--- a/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
+++ b/src/main/java/org/olat/ims/qti21/model/xml/AssessmentItemFactory.java
@@ -19,6 +19,14 @@
  */
 package org.olat.ims.qti21.model.xml;
 
+import static org.olat.ims.qti21.QTI21Constants.MAXSCORE_CLX_IDENTIFIER;
+import static org.olat.ims.qti21.QTI21Constants.MINSCORE_CLX_IDENTIFIER;
+import static org.olat.ims.qti21.QTI21Constants.SCORE_CLX_IDENTIFIER;
+import static org.olat.ims.qti21.QTI21Constants.SCORE_IDENTIFIER;
+
+import java.util.ArrayList;
+import java.util.List;
+
 import org.olat.core.helpers.Settings;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.model.IdentifierGenerator;
@@ -27,21 +35,27 @@ import uk.ac.ed.ph.jqtiplus.group.NodeGroupList;
 import uk.ac.ed.ph.jqtiplus.group.item.ItemBodyGroup;
 import uk.ac.ed.ph.jqtiplus.group.item.interaction.PromptGroup;
 import uk.ac.ed.ph.jqtiplus.group.item.interaction.choice.SimpleChoiceGroup;
-import uk.ac.ed.ph.jqtiplus.group.item.response.declaration.ResponseDeclarationGroup;
 import uk.ac.ed.ph.jqtiplus.group.item.response.processing.ResponseProcessingGroup;
 import uk.ac.ed.ph.jqtiplus.group.outcome.declaration.OutcomeDeclarationGroup;
 import uk.ac.ed.ph.jqtiplus.node.QtiNode;
 import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
 import uk.ac.ed.ph.jqtiplus.node.content.basic.TextRun;
 import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
 import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue;
 import uk.ac.ed.ph.jqtiplus.node.expression.general.Correct;
 import uk.ac.ed.ph.jqtiplus.node.expression.general.Variable;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.And;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.Gt;
 import uk.ac.ed.ph.jqtiplus.node.expression.operator.IsNull;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.Lt;
 import uk.ac.ed.ph.jqtiplus.node.expression.operator.Match;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.Multiple;
 import uk.ac.ed.ph.jqtiplus.node.expression.operator.Sum;
 import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
 import uk.ac.ed.ph.jqtiplus.node.item.CorrectResponse;
+import uk.ac.ed.ph.jqtiplus.node.item.ModalFeedback;
 import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
 import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice;
 import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration;
@@ -50,10 +64,13 @@ import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElse;
 import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElseIf;
 import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseIf;
 import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseProcessing;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseRule;
 import uk.ac.ed.ph.jqtiplus.node.item.response.processing.SetOutcomeValue;
 import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration;
 import uk.ac.ed.ph.jqtiplus.node.shared.FieldValue;
 import uk.ac.ed.ph.jqtiplus.node.shared.declaration.DefaultValue;
+import uk.ac.ed.ph.jqtiplus.node.test.View;
+import uk.ac.ed.ph.jqtiplus.node.test.VisibilityMode;
 import uk.ac.ed.ph.jqtiplus.types.ComplexReferenceIdentifier;
 import uk.ac.ed.ph.jqtiplus.types.Identifier;
 import uk.ac.ed.ph.jqtiplus.value.BaseType;
@@ -81,23 +98,11 @@ public class AssessmentItemFactory {
 		
 		NodeGroupList nodeGroups = assessmentItem.getNodeGroups();
 
-		String responseDeclarationId = "RESPONSE_1";
-		//define correct answer
-		ResponseDeclarationGroup responseDeclarations = nodeGroups.getResponseDeclarationGroup();
-		ResponseDeclaration responseDeclaration = new ResponseDeclaration(assessmentItem);
-		responseDeclaration.setIdentifier(Identifier.parseString(responseDeclarationId));
-		responseDeclaration.setCardinality(Cardinality.SINGLE);
-		responseDeclaration.setBaseType(BaseType.IDENTIFIER);
-		responseDeclarations.getResponseDeclarations().add(responseDeclaration);
-		
-		CorrectResponse correctResponse = new CorrectResponse(responseDeclaration);
-		responseDeclaration.setCorrectResponse(correctResponse);
-		
+		Identifier responseDeclarationId = Identifier.assumedLegal("RESPONSE_1");
 		Identifier correctResponseId = IdentifierGenerator.newAsIdentifier();
-		FieldValue fieldValue = new FieldValue(correctResponse);
-		IdentifierValue identifierValue = new IdentifierValue(correctResponseId);
-		fieldValue.setSingleValue(identifierValue);
-		correctResponse.getFieldValues().add(fieldValue);
+		//define correct answer
+		ResponseDeclaration responseDeclaration = createSingleChoiceCorrectResponseDeclaration(assessmentItem, responseDeclarationId, correctResponseId);
+		nodeGroups.getResponseDeclarationGroup().getResponseDeclarations().add(responseDeclaration);
 		
 		//outcomes
 		OutcomeDeclarationGroup outcomeDeclarations = nodeGroups.getOutcomeDeclarationGroup();
@@ -107,11 +112,11 @@ public class AssessmentItemFactory {
 		outcomeDeclarations.getOutcomeDeclarations().add(scoreOutcomeDeclaration);
 
 		// outcome max score
-		OutcomeDeclaration maxScoreOutcomeDeclaration = createOutcomeDeclarationForMaxScore(assessmentItem);
+		OutcomeDeclaration maxScoreOutcomeDeclaration = createOutcomeDeclarationForMaxScore(assessmentItem, 1.0d);
 		outcomeDeclarations.getOutcomeDeclarations().add(maxScoreOutcomeDeclaration);
 
 		// outcome feedback
-		OutcomeDeclaration feedbackOutcomeDeclaration = createOutcomeDeclarationForFeedback(assessmentItem);
+		OutcomeDeclaration feedbackOutcomeDeclaration = createOutcomeDeclarationForFeedbackBasic(assessmentItem);
 		outcomeDeclarations.getOutcomeDeclarations().add(feedbackOutcomeDeclaration);
 		
 		//the single choice interaction
@@ -126,7 +131,7 @@ public class AssessmentItemFactory {
 		ChoiceInteraction choiceInteraction = new ChoiceInteraction(itemBody);
 		choiceInteraction.setMaxChoices(1);
 		choiceInteraction.setShuffle(true);
-		choiceInteraction.setResponseIdentifier(Identifier.parseString(responseDeclarationId));
+		choiceInteraction.setResponseIdentifier(responseDeclarationId);
 		itemBodyGroup.getItemBody().getBlocks().add(choiceInteraction);
 		
 		PromptGroup prompts = new PromptGroup(choiceInteraction);
@@ -150,6 +155,22 @@ public class AssessmentItemFactory {
 		return assessmentItem;
 	}
 	
+	public static ResponseDeclaration createSingleChoiceCorrectResponseDeclaration(AssessmentItem assessmentItem, Identifier declarationId, Identifier correctResponseId) {
+		ResponseDeclaration responseDeclaration = new ResponseDeclaration(assessmentItem);
+		responseDeclaration.setIdentifier(declarationId);
+		responseDeclaration.setCardinality(Cardinality.SINGLE);
+		responseDeclaration.setBaseType(BaseType.IDENTIFIER);
+
+		CorrectResponse correctResponse = new CorrectResponse(responseDeclaration);
+		responseDeclaration.setCorrectResponse(correctResponse);
+		
+		FieldValue fieldValue = new FieldValue(correctResponse);
+		IdentifierValue identifierValue = new IdentifierValue(correctResponseId);
+		fieldValue.setSingleValue(identifierValue);
+		correctResponse.getFieldValues().add(fieldValue);
+		return responseDeclaration;
+	}
+	
 	public static OutcomeDeclaration createOutcomeDeclarationForScore(AssessmentItem assessmentItem) {
 		OutcomeDeclaration scoreOutcomeDeclaration = new OutcomeDeclaration(assessmentItem);
 		scoreOutcomeDeclaration.setIdentifier(QTI21Constants.SCORE_IDENTIFIER);
@@ -165,7 +186,49 @@ public class AssessmentItemFactory {
 		return scoreOutcomeDeclaration;
 	}
 	
-	public static OutcomeDeclaration createOutcomeDeclarationForMaxScore(AssessmentItem assessmentItem) {
+	/**
+	 * Rule which ensure that the final score is not above the max. score value.
+	 */
+	public static ResponseRule createMaxScoreBoundLimitRule(ResponseProcessing responseProcessing) {
+		/*
+		<responseCondition>
+			<responseIf>
+				<gt>
+					<variable identifier="SCORE" /><variable identifier="MAXSCORE" />
+				</gt>
+				<setOutcomeValue identifier="SCORE">
+					<variable identifier="MAXSCORE" />
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		*/
+		ResponseCondition rule = new ResponseCondition(responseProcessing);
+		ResponseIf responseIf = new ResponseIf(rule);
+		rule.setResponseIf(responseIf);
+		
+		Gt gt = new Gt(responseIf);
+		responseIf.setExpression(gt);
+		
+		Variable scoreVar = new Variable(gt);
+		scoreVar.setIdentifier(SCORE_CLX_IDENTIFIER);
+		gt.getExpressions().add(scoreVar);
+		
+		Variable maxScoreVar = new Variable(gt);
+		maxScoreVar.setIdentifier(MAXSCORE_CLX_IDENTIFIER);
+		gt.getExpressions().add(maxScoreVar);
+		
+		SetOutcomeValue setOutcomeValue = new SetOutcomeValue(responseIf);
+		setOutcomeValue.setIdentifier(SCORE_IDENTIFIER);
+		
+		Variable maxScoreOutcomeVar = new Variable(setOutcomeValue);
+		maxScoreOutcomeVar.setIdentifier(MAXSCORE_CLX_IDENTIFIER);
+		setOutcomeValue.setExpression(maxScoreOutcomeVar);
+		responseIf.getResponseRules().add(setOutcomeValue);
+		
+		return rule;
+	}
+	
+	public static OutcomeDeclaration createOutcomeDeclarationForMaxScore(AssessmentItem assessmentItem, double maxScore) {
 		OutcomeDeclaration maxScoreOutcomeDeclaration = new OutcomeDeclaration(assessmentItem);
 		maxScoreOutcomeDeclaration.setIdentifier(QTI21Constants.MAXSCORE_IDENTIFIER);
 		maxScoreOutcomeDeclaration.setCardinality(Cardinality.SINGLE);
@@ -174,13 +237,74 @@ public class AssessmentItemFactory {
 		DefaultValue maxScoreDefaultVal = new DefaultValue(maxScoreOutcomeDeclaration);
 		maxScoreOutcomeDeclaration.setDefaultValue(maxScoreDefaultVal);
 		
-		FieldValue maxScoreDefaultFieldVal = new FieldValue(maxScoreDefaultVal, new FloatValue(1.0f));
+		FieldValue maxScoreDefaultFieldVal = new FieldValue(maxScoreDefaultVal, new FloatValue(maxScore));
+		maxScoreDefaultVal.getFieldValues().add(maxScoreDefaultFieldVal);
+		
+		return maxScoreOutcomeDeclaration;
+	}
+	
+	/**
+	 * Rule which ensure that the final score is not under the min. score value.
+	 */
+	public static ResponseRule createMinScoreBoundLimitRule(ResponseProcessing responseProcessing) {
+		/*
+		<responseCondition>
+			<responseIf>
+				<lt>
+					<variable identifier="SCORE" /><variable identifier="MINSCORE" />
+				</lt>
+				<setOutcomeValue identifier="SCORE">
+					<variable identifier="MINSCORE" />
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		*/
+		ResponseCondition rule = new ResponseCondition(responseProcessing);
+		ResponseIf responseIf = new ResponseIf(rule);
+		rule.setResponseIf(responseIf);
+		
+		Lt lt = new Lt(responseIf);
+		responseIf.setExpression(lt);
+		
+		Variable scoreVar = new Variable(lt);
+		scoreVar.setIdentifier(SCORE_CLX_IDENTIFIER);
+		lt.getExpressions().add(scoreVar);
+		
+		Variable minScoreVar = new Variable(lt);
+		minScoreVar.setIdentifier(MINSCORE_CLX_IDENTIFIER);
+		lt.getExpressions().add(minScoreVar);
+		
+		SetOutcomeValue setOutcomeValue = new SetOutcomeValue(responseIf);
+		setOutcomeValue.setIdentifier(SCORE_IDENTIFIER);
+		
+		Variable minScoreOutcomeVar = new Variable(setOutcomeValue);
+		minScoreOutcomeVar.setIdentifier(MINSCORE_CLX_IDENTIFIER);
+		setOutcomeValue.setExpression(minScoreOutcomeVar);
+		responseIf.getResponseRules().add(setOutcomeValue);
+
+		return rule;
+	}
+	
+	public static OutcomeDeclaration createOutcomeDeclarationForMinScore(AssessmentItem assessmentItem, double minScore) {
+		OutcomeDeclaration maxScoreOutcomeDeclaration = new OutcomeDeclaration(assessmentItem);
+		maxScoreOutcomeDeclaration.setIdentifier(QTI21Constants.MINSCORE_IDENTIFIER);
+		maxScoreOutcomeDeclaration.setCardinality(Cardinality.SINGLE);
+		maxScoreOutcomeDeclaration.setBaseType(BaseType.FLOAT);
+		
+		List<View> views = new ArrayList<>();
+		views.add(View.TEST_CONSTRUCTOR);
+		maxScoreOutcomeDeclaration.setViews(views);
+
+		DefaultValue maxScoreDefaultVal = new DefaultValue(maxScoreOutcomeDeclaration);
+		maxScoreOutcomeDeclaration.setDefaultValue(maxScoreDefaultVal);
+		
+		FieldValue maxScoreDefaultFieldVal = new FieldValue(maxScoreDefaultVal, new FloatValue(minScore));
 		maxScoreDefaultVal.getFieldValues().add(maxScoreDefaultFieldVal);
 		
 		return maxScoreOutcomeDeclaration;
 	}
 	
-	public static OutcomeDeclaration createOutcomeDeclarationForFeedback(AssessmentItem assessmentItem) {
+	public static OutcomeDeclaration createOutcomeDeclarationForFeedbackBasic(AssessmentItem assessmentItem) {
 		OutcomeDeclaration feedbackOutcomeDeclaration = new OutcomeDeclaration(assessmentItem);
 		feedbackOutcomeDeclaration.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
 		feedbackOutcomeDeclaration.setCardinality(Cardinality.SINGLE);
@@ -192,10 +316,125 @@ public class AssessmentItemFactory {
 		FieldValue feedbackDefaultFieldVal = new FieldValue(feedbackDefaultVal, new IdentifierValue("empty"));
 		feedbackDefaultVal.getFieldValues().add(feedbackDefaultFieldVal);
 		
+		List<View> views = new ArrayList<>();
+		views.add(View.TEST_CONSTRUCTOR);
+		feedbackOutcomeDeclaration.setViews(views);
+		
+		return feedbackOutcomeDeclaration;
+	}
+	
+	public static OutcomeDeclaration createOutcomeDeclarationForFeedbackModal(AssessmentItem assessmentItem) {
+		OutcomeDeclaration feedbackOutcomeDeclaration = new OutcomeDeclaration(assessmentItem);
+		feedbackOutcomeDeclaration.setIdentifier(QTI21Constants.FEEDBACKMODAL_IDENTIFIER);
+		feedbackOutcomeDeclaration.setCardinality(Cardinality.MULTIPLE);
+		feedbackOutcomeDeclaration.setBaseType(BaseType.IDENTIFIER);
+		
+		List<View> views = new ArrayList<>();
+		views.add(View.TEST_CONSTRUCTOR);
+		feedbackOutcomeDeclaration.setViews(views);
+
 		return feedbackOutcomeDeclaration;
 	}
 	
-	public static ResponseProcessing createResponseProcessing(AssessmentItem assessmentItem, String responseId) {
+	public static ChoiceInteraction createSingleChoiceInteraction(AssessmentItem assessmentItem, Identifier responseDeclarationId) {
+		ChoiceInteraction choiceInteraction = new ChoiceInteraction(assessmentItem.getItemBody());
+		choiceInteraction.setMaxChoices(1);
+		choiceInteraction.setShuffle(true);
+		choiceInteraction.setResponseIdentifier(responseDeclarationId);
+		
+		PromptGroup prompts = new PromptGroup(choiceInteraction);
+		choiceInteraction.getNodeGroups().add(prompts);
+		
+		SimpleChoiceGroup singleChoices = new SimpleChoiceGroup(choiceInteraction);
+		choiceInteraction.getNodeGroups().add(singleChoices);
+		return choiceInteraction;
+	}
+
+	public static ModalFeedback createModalFeedback(AssessmentItem assessmentItem, Identifier identifier, String title, String text) {
+		/*
+		<modalFeedback identifier="Feedback1041659806" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="Wrong answer">
+			<p>Feedback answer</p>
+		</modalFeedback>
+		*/
+		
+		ModalFeedback modalFeedback = new ModalFeedback(assessmentItem);
+		modalFeedback.setIdentifier(identifier);
+		modalFeedback.setOutcomeIdentifier(QTI21Constants.FEEDBACKMODAL_IDENTIFIER);
+		modalFeedback.setVisibilityMode(VisibilityMode.parseVisibilityMode("show"));
+		modalFeedback.getAttributes().getStringAttribute(ModalFeedback.ATTR_TITLE_NAME).setValue(title);
+		
+		List<Block> blocks = new AssessmentBuilderHelper().parseHtml(text);
+		for(Block block:blocks) {
+			if(block instanceof FlowStatic) {
+				modalFeedback.getFlowStatics().add((FlowStatic)block);
+			}
+		}
+
+		return modalFeedback;
+	}
+	
+	public static ResponseCondition createModalFeedbackBasicRule(ResponseProcessing responseProcessing, Identifier feedbackIdentifier, String inCorrect) {
+		ResponseCondition rule = new ResponseCondition(responseProcessing);
+		/*
+		<responseIf>
+			<and>
+				<match>
+					<baseValue baseType="identifier">correct</baseValue>
+					<variable identifier="FEEDBACKBASIC" />
+				</match>
+			</and>
+			<setOutcomeValue identifier="FEEDBACKMODAL">
+				<multiple>
+					<variable identifier="FEEDBACKMODAL" />
+					<baseValue baseType="identifier">Feedback261171147</baseValue>
+				</multiple>
+			</setOutcomeValue>
+		</responseIf>
+		*/
+		
+		ResponseIf responseIf = new ResponseIf(rule);
+		rule.setResponseIf(responseIf);
+		
+		{//rule
+			And and = new And(responseIf);
+			responseIf.getExpressions().add(and);
+			
+			Match match = new Match(and);
+			and.getExpressions().add(match);
+			
+			BaseValue feedbackVal = new BaseValue(match);
+			feedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
+			feedbackVal.setSingleValue(new IdentifierValue(inCorrect));
+			match.getExpressions().add(feedbackVal);
+			
+			Variable variable = new Variable(match);
+			variable.setIdentifier(ComplexReferenceIdentifier.parseString(QTI21Constants.FEEDBACKBASIC));
+			match.getExpressions().add(variable);
+		}
+
+		{//outcome
+			SetOutcomeValue feedbackVar = new SetOutcomeValue(responseIf);
+			feedbackVar.setIdentifier(QTI21Constants.FEEDBACKMODAL_IDENTIFIER);
+			
+			Multiple multiple = new Multiple(feedbackVar);
+			feedbackVar.setExpression(multiple);
+			
+			Variable variable = new Variable(multiple);
+			variable.setIdentifier(ComplexReferenceIdentifier.parseString(QTI21Constants.FEEDBACKMODAL));
+			multiple.getExpressions().add(variable);
+			
+			BaseValue feedbackVal = new BaseValue(feedbackVar);
+			feedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
+			feedbackVal.setSingleValue(new IdentifierValue(feedbackIdentifier));
+			multiple.getExpressions().add(feedbackVal);
+			
+			responseIf.getResponseRules().add(feedbackVar);
+		}
+		
+		return rule;
+	}
+	
+	public static ResponseProcessing createResponseProcessing(AssessmentItem assessmentItem, Identifier responseId) {
 		ResponseProcessing responseProcessing = new ResponseProcessing(assessmentItem);
 
 		ResponseCondition rule = new ResponseCondition(responseProcessing);
@@ -208,7 +447,7 @@ public class AssessmentItemFactory {
 		responseIf.getExpressions().add(isNull);
 		
 		Variable variable = new Variable(isNull);
-		variable.setIdentifier(ComplexReferenceIdentifier.parseString(responseId));
+		variable.setIdentifier(ComplexReferenceIdentifier.parseString(responseId.toString()));
 		isNull.getExpressions().add(variable);
 		
 		{
@@ -231,11 +470,11 @@ public class AssessmentItemFactory {
 			responseElseIf.getExpressions().add(match);
 			
 			Variable responseVar = new Variable(match);
-			responseVar.setIdentifier(ComplexReferenceIdentifier.parseString(responseId));
+			responseVar.setIdentifier(ComplexReferenceIdentifier.parseString(responseId.toString()));
 			match.getExpressions().add(responseVar);
 			
 			Correct correct = new Correct(match);
-			correct.setIdentifier(ComplexReferenceIdentifier.parseString(responseId));
+			correct.setIdentifier(ComplexReferenceIdentifier.parseString(responseId.toString()));
 			match.getExpressions().add(correct);
 		}
 
@@ -263,7 +502,7 @@ public class AssessmentItemFactory {
 			correctFeedbackVar.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
 			BaseValue correctFeedbackVal = new BaseValue(correctFeedbackVar);
 			correctFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
-			correctFeedbackVal.setSingleValue(QTI21Constants.CORRECT);
+			correctFeedbackVal.setSingleValue(QTI21Constants.CORRECT_IDENTIFIER_VALUE);
 			correctFeedbackVar.setExpression(correctFeedbackVal);
 			responseElseIf.getResponseRules().add(correctFeedbackVar);
 		}
@@ -276,7 +515,7 @@ public class AssessmentItemFactory {
 			incorrectFeedbackVar.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
 			BaseValue incorrectFeedbackVal = new BaseValue(incorrectFeedbackVar);
 			incorrectFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
-			incorrectFeedbackVal.setSingleValue(QTI21Constants.INCORRECT);
+			incorrectFeedbackVal.setSingleValue(QTI21Constants.INCORRECT_IDENTIFIER_VALUE);
 			incorrectFeedbackVar.setExpression(incorrectFeedbackVal);
 			responseElse.getResponseRules().add(incorrectFeedbackVar);
 		}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/ChoiceAssessmentItemBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..82303f8962146770e36a560b7d57756afaa77c24
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/ChoiceAssessmentItemBuilder.java
@@ -0,0 +1,113 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.transform.stream.StreamResult;
+
+import org.olat.core.gui.render.StringOutput;
+
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
+
+/**
+ * 
+ * Initial date: 08.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class ChoiceAssessmentItemBuilder extends AssessmentItemBuilder {
+	
+	protected boolean shuffle;
+	protected String question;
+	protected List<SimpleChoice> choices;
+	protected Identifier responseIdentifier;
+	protected ChoiceInteraction choiceInteraction;
+	
+	public ChoiceAssessmentItemBuilder(AssessmentItem assessmentItem, QtiSerializer qtiSerializer) {
+		super(assessmentItem, qtiSerializer);
+	}
+	
+	@Override
+	public void extract() {
+		super.extract();
+		extractChoiceInteraction();
+	}
+	
+	private void extractChoiceInteraction() {
+		StringOutput sb = new StringOutput();
+		List<Block> blocks = assessmentItem.getItemBody().getBlocks();
+		for(Block block:blocks) {
+			if(block instanceof ChoiceInteraction) {
+				choiceInteraction = (ChoiceInteraction)block;
+				responseIdentifier = choiceInteraction.getResponseIdentifier();
+				shuffle = choiceInteraction.getShuffle();
+				break;
+			} else {
+				qtiSerializer.serializeJqtiObject(block, new StreamResult(sb));
+			}
+		}
+		question = sb.toString();
+		
+		choices = new ArrayList<>();
+		if(choiceInteraction != null) {
+			choices.addAll(choiceInteraction.getSimpleChoices());
+		}
+	}
+
+	public ChoiceInteraction getChoiceInteraction() {
+		return choiceInteraction;
+	}
+	
+	public boolean isShuffle() {
+		return shuffle;
+	}
+	
+	public void setShuffle(boolean shuffle) {
+		this.shuffle = shuffle;
+	}
+	
+	/**
+	 * Return the HTML block before the choice interaction as a string.
+	 * 
+	 * @return
+	 */
+	public String getQuestion() {
+		return question;
+	}
+	
+	public void setQuestion(String html) {
+		this.question = html;
+	}
+	
+	public List<SimpleChoice> getSimpleChoices() {
+		return choices;
+	}
+	
+	public void setSimpleChoices(List<SimpleChoice> choices) {
+		this.choices = new ArrayList<>(choices);
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c5eb63f61203eb1b93b6c303428a49bec066b0b
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/ModalFeedbackBuilder.java
@@ -0,0 +1,179 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.util.List;
+
+import org.olat.ims.qti21.QTI21Constants;
+
+import uk.ac.ed.ph.jqtiplus.attribute.value.StringAttribute;
+import uk.ac.ed.ph.jqtiplus.node.expression.Expression;
+import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue;
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.item.ModalFeedback;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseCondition;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseIf;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseRule;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.SetOutcomeValue;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
+import uk.ac.ed.ph.jqtiplus.value.IdentifierValue;
+import uk.ac.ed.ph.jqtiplus.value.SingleValue;
+
+/**
+ * 
+ * Initial date: 09.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ModalFeedbackBuilder {
+	
+	private final ModalFeedback modalFeedback;
+	private final AssessmentItem assessmentItem;
+	
+	private String title;
+	private String text;
+	private Identifier identifier;
+	
+	public ModalFeedbackBuilder(AssessmentItem assessmentItem, ModalFeedback modalFeedback) {
+		this.assessmentItem = assessmentItem;
+		this.modalFeedback = modalFeedback;
+		if(modalFeedback != null) {
+			text = new AssessmentBuilderHelper().toString(modalFeedback.getFlowStatics());
+			StringAttribute titleAttr = modalFeedback.getAttributes().getStringAttribute(ModalFeedback.ATTR_TITLE_NAME);
+			title = titleAttr == null ? null : titleAttr.getComputedValue();
+			identifier = modalFeedback.getIdentifier();
+			
+		}
+	}
+	
+	public Identifier getModalFeedbackIdentifier() {
+		return modalFeedback.getIdentifier();
+	}
+	
+	public ResponseRule getResponseRule() {
+		ResponseRule feedbackRule = findFeedbackRule(modalFeedback.getIdentifier());
+		return feedbackRule;
+	}
+	
+	public boolean isCorrectRule() {
+		ResponseRule feedbackRule = findFeedbackRule(modalFeedback.getIdentifier());
+		return findFeedbackRule(feedbackRule, QTI21Constants.CORRECT_IDENTIFIER);
+	}
+	
+	public boolean isIncorrectRule() {
+		ResponseRule feedbackRule = findFeedbackRule(modalFeedback.getIdentifier());
+		return findFeedbackRule(feedbackRule, QTI21Constants.INCORRECT_IDENTIFIER);
+	}
+	
+	public String getTitle() {
+		return title;
+	}
+	
+	public void setTitle(String title) {
+		this.title = title;
+	}
+	
+	public String getText() {
+		return text;
+	}
+	
+	public void setText(String text) {
+		this.text = text;
+	}
+	
+	public Identifier getIdentifier() {
+		return identifier;
+	}
+
+	public void setIdentifier(Identifier identifier) {
+		this.identifier = identifier;
+	}
+
+	private ResponseRule findFeedbackRule(Identifier feedbackIdentifier) {
+		List<ResponseRule> responseRules = assessmentItem.getResponseProcessing().getResponseRules();
+		for(ResponseRule responseRule:responseRules) {
+			if(responseRule instanceof ResponseCondition) {
+				if(findFeedbackRuleInSetOutcomeVariable(responseRule, feedbackIdentifier)) {
+					return responseRule;
+				}
+			}
+		}
+		return null;
+	}
+	
+	private boolean findFeedbackRuleInSetOutcomeVariable(ResponseRule responseRule, Identifier feedbackIdentifier) {
+		if(responseRule instanceof ResponseCondition) {
+			ResponseCondition responseCondition = (ResponseCondition)responseRule;
+			ResponseIf responseIf = responseCondition.getResponseIf();
+			List<ResponseRule> ifResponseRules = responseIf.getResponseRules();
+			for(ResponseRule ifResponseRule:ifResponseRules) {
+				if(ifResponseRule instanceof SetOutcomeValue) {
+					SetOutcomeValue setOutcomeValue = (SetOutcomeValue)ifResponseRule;
+					if(findFeedbackRuleInExpression(setOutcomeValue.getExpression(), feedbackIdentifier)) {
+						return true;
+					}
+				}
+			}
+		}
+		return false;
+	}
+	
+	private boolean findFeedbackRule(ResponseRule responseRule, Identifier id) {
+		if(responseRule instanceof ResponseCondition) {
+			ResponseCondition responseCondition = (ResponseCondition)responseRule;
+			ResponseIf responseIf = responseCondition.getResponseIf();
+			List<Expression> expressions = responseIf.getExpressions();
+			if(findFeedbackRuleInExpression(expressions, id)) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	private boolean findFeedbackRuleInExpression(List<Expression> expressions, Identifier feedbackIdentifier) {
+		for(Expression expression:expressions) {
+			if(findFeedbackRuleInExpression(expression, feedbackIdentifier)) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	private boolean findFeedbackRuleInExpression(Expression expression, Identifier feedbackIdentifier) {
+		if(expression instanceof BaseValue) {
+			BaseValue bValue = (BaseValue)expression;
+			SingleValue sValue = bValue.getSingleValue();
+			if(sValue instanceof IdentifierValue) {
+				IdentifierValue iValue = (IdentifierValue)sValue;
+				if(feedbackIdentifier.equals(iValue.identifierValue())) {
+					return true;
+				}
+			}
+		} else {
+			List<Expression> childExpressions = expression.getExpressions();
+			for(Expression childExpression:childExpressions) {
+				if(findFeedbackRuleInExpression(childExpression, feedbackIdentifier)) {
+					return true;
+				}
+			}
+		}
+		return false;
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/ScoreBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/ScoreBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..d440289bfd1363f76b7e21b71e8837d6ea5e4227
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/ScoreBuilder.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import uk.ac.ed.ph.jqtiplus.node.outcome.declaration.OutcomeDeclaration;
+
+/**
+ * 
+ * Initial date: 10.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ScoreBuilder {
+	
+	private Double score;
+	private OutcomeDeclaration outcomeDeclaration;
+	
+	public ScoreBuilder(Double score, OutcomeDeclaration outcomeDeclaration) {
+		this.score = score;
+		this.outcomeDeclaration = outcomeDeclaration;
+	}
+
+	public Double getScore() {
+		return score;
+	}
+
+	public void setScore(Double score) {
+		this.score = score;
+	}
+
+	public OutcomeDeclaration getOutcomeDeclaration() {
+		return outcomeDeclaration;
+	}
+
+	public void setOutcomeDeclaration(OutcomeDeclaration outcomeDeclaration) {
+		this.outcomeDeclaration = outcomeDeclaration;
+	}
+	
+	
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java b/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java
new file mode 100644
index 0000000000000000000000000000000000000000..57381148a5a06607c095ed6b2b6a699e29c9ecad
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/model/xml/SingleChoiceAssessmentItemBuilder.java
@@ -0,0 +1,244 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.util.List;
+
+import org.olat.ims.qti21.QTI21Constants;
+
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.expression.general.BaseValue;
+import uk.ac.ed.ph.jqtiplus.node.expression.general.Correct;
+import uk.ac.ed.ph.jqtiplus.node.expression.general.Variable;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.IsNull;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.Match;
+import uk.ac.ed.ph.jqtiplus.node.expression.operator.Sum;
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.node.item.CorrectResponse;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice;
+import uk.ac.ed.ph.jqtiplus.node.item.response.declaration.ResponseDeclaration;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseCondition;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElse;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseElseIf;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseIf;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.ResponseRule;
+import uk.ac.ed.ph.jqtiplus.node.item.response.processing.SetOutcomeValue;
+import uk.ac.ed.ph.jqtiplus.node.shared.FieldValue;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.types.ComplexReferenceIdentifier;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
+import uk.ac.ed.ph.jqtiplus.value.BaseType;
+import uk.ac.ed.ph.jqtiplus.value.Cardinality;
+import uk.ac.ed.ph.jqtiplus.value.IdentifierValue;
+import uk.ac.ed.ph.jqtiplus.value.Value;
+
+/**
+ * 
+ * Initial date: 08.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SingleChoiceAssessmentItemBuilder extends ChoiceAssessmentItemBuilder {
+	
+	private Identifier correctAnswer;
+	
+	public SingleChoiceAssessmentItemBuilder(AssessmentItem assessmentItem, QtiSerializer qtiSerializer) {
+		super(assessmentItem, qtiSerializer);
+	}
+	
+	@Override
+	public void extract() {
+		super.extract();
+		
+		List<ResponseDeclaration> responseDeclarations = assessmentItem.getResponseDeclarations();
+		if(responseDeclarations.size() == 1) {
+			CorrectResponse correctResponse = responseDeclarations.get(0).getCorrectResponse();
+			if(correctResponse != null) {
+				List<FieldValue> values = correctResponse.getFieldValues();
+				Value value = FieldValue.computeValue(Cardinality.SINGLE, values);
+				if(value instanceof IdentifierValue) {
+					IdentifierValue identifierValue = (IdentifierValue)value;
+					correctAnswer = identifierValue.identifierValue();
+				}
+			}
+		}
+	}
+	
+	public boolean isCorrect(SimpleChoice choice) {
+		return correctAnswer != null && correctAnswer.equals(choice.getIdentifier());
+	}
+	
+	public void setCorrectAnswer(Identifier identifier) {
+		correctAnswer = identifier;
+	}
+
+	@Override
+	protected void buildResponseDeclaration() {
+		ResponseDeclaration responseDeclaration = AssessmentItemFactory
+				.createSingleChoiceCorrectResponseDeclaration(assessmentItem, responseIdentifier, correctAnswer);
+		assessmentItem.getResponseDeclarations().add(responseDeclaration);
+	}
+	
+	@Override
+	protected void buildItemBody() {
+		//remove current blocks
+		List<Block> blocks = assessmentItem.getItemBody().getBlocks();
+		blocks.clear();
+
+		//add question
+		List<Block> questionBlocks = getHelper().parseHtml(question);
+		blocks.addAll(questionBlocks);
+		
+		//add interaction
+		ChoiceInteraction singleChoiceInteraction = AssessmentItemFactory
+				.createSingleChoiceInteraction(assessmentItem, responseIdentifier);
+		singleChoiceInteraction.setShuffle(isShuffle());
+		blocks.add(singleChoiceInteraction);
+		List<SimpleChoice> choiceList = getSimpleChoices();
+		singleChoiceInteraction.getSimpleChoices().addAll(choiceList);
+	}
+
+	@Override
+	protected void buildMainScoreRule(List<ResponseRule> responseRules) {
+		ResponseCondition rule = new ResponseCondition(assessmentItem.getResponseProcessing());
+		responseRules.add(0, rule);
+		/*
+			<responseIf>
+				<isNull>
+					<variable identifier="RESPONSE_1" />
+				</isNull>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						empty
+					</baseValue>
+				</setOutcomeValue>
+			</responseIf>
+		*/
+		
+		//if no response
+		ResponseIf responseIf = new ResponseIf(rule);
+		rule.setResponseIf(responseIf);
+		
+		IsNull isNull = new IsNull(responseIf);
+		responseIf.getExpressions().add(isNull);
+		
+		Variable variable = new Variable(isNull);
+		variable.setIdentifier(ComplexReferenceIdentifier.parseString(responseIdentifier.toString()));
+		isNull.getExpressions().add(variable);
+		
+		{
+			SetOutcomeValue feedbackVar = new SetOutcomeValue(responseIf);
+			feedbackVar.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
+			BaseValue feedbackVal = new BaseValue(feedbackVar);
+			feedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
+			feedbackVal.setSingleValue(new IdentifierValue("empty"));
+			feedbackVar.setExpression(feedbackVal);
+			responseIf.getResponseRules().add(feedbackVar);
+		}
+		/*
+			<responseElseIf>
+				<match>
+					<variable identifier="RESPONSE_1" /><correct identifier="RESPONSE_1" />
+				</match>
+				<setOutcomeValue identifier="SCORE">
+					<sum>
+						<variable identifier="SCORE" /><variable identifier="MAXSCORE" />
+					</sum>
+				</setOutcomeValue>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						correct
+					</baseValue>
+				</setOutcomeValue>
+			</responseElseIf>
+		*/
+		
+		//else if correct response
+		ResponseElseIf responseElseIf = new ResponseElseIf(rule);
+		rule.getResponseElseIfs().add(responseElseIf);
+		
+		//match 
+		{
+			Match match = new Match(responseElseIf);
+			responseElseIf.getExpressions().add(match);
+			
+			Variable responseVar = new Variable(match);
+			responseVar.setIdentifier(ComplexReferenceIdentifier.parseString(responseIdentifier.toString()));
+			match.getExpressions().add(responseVar);
+			
+			Correct correct = new Correct(match);
+			correct.setIdentifier(ComplexReferenceIdentifier.parseString(responseIdentifier.toString()));
+			match.getExpressions().add(correct);
+		}
+
+		// outcome score
+		{
+			SetOutcomeValue scoreOutcomeVar = new SetOutcomeValue(responseIf);
+			scoreOutcomeVar.setIdentifier(QTI21Constants.SCORE_IDENTIFIER);
+			responseElseIf.getResponseRules().add(scoreOutcomeVar);
+			
+			Sum sum = new Sum(scoreOutcomeVar);
+			scoreOutcomeVar.getExpressions().add(sum);
+			
+			Variable scoreVar = new Variable(sum);
+			scoreVar.setIdentifier(QTI21Constants.SCORE_CLX_IDENTIFIER);
+			sum.getExpressions().add(scoreVar);
+			
+			Variable maxScoreVar = new Variable(sum);
+			maxScoreVar.setIdentifier(QTI21Constants.MAXSCORE_CLX_IDENTIFIER);
+			sum.getExpressions().add(maxScoreVar);
+		}
+		
+		// outcome feedback
+		{
+			SetOutcomeValue correctFeedbackVar = new SetOutcomeValue(responseIf);
+			correctFeedbackVar.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
+			BaseValue correctFeedbackVal = new BaseValue(correctFeedbackVar);
+			correctFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
+			correctFeedbackVal.setSingleValue(QTI21Constants.CORRECT_IDENTIFIER_VALUE);
+			correctFeedbackVar.setExpression(correctFeedbackVal);
+			responseElseIf.getResponseRules().add(correctFeedbackVar);
+		}
+		
+		/*
+			<responseElse>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						incorrect
+					</baseValue>
+				</setOutcomeValue>
+			</responseElse>
+		</responseCondition>
+		*/
+		
+		ResponseElse responseElse = new ResponseElse(rule);
+		rule.setResponseElse(responseElse);
+		{// feedback incorrect
+			SetOutcomeValue incorrectFeedbackVar = new SetOutcomeValue(responseIf);
+			incorrectFeedbackVar.setIdentifier(QTI21Constants.FEEDBACKBASIC_IDENTIFIER);
+			BaseValue incorrectFeedbackVal = new BaseValue(incorrectFeedbackVar);
+			incorrectFeedbackVal.setBaseTypeAttrValue(BaseType.IDENTIFIER);
+			incorrectFeedbackVal.setSingleValue(QTI21Constants.INCORRECT_IDENTIFIER_VALUE);
+			incorrectFeedbackVar.setExpression(incorrectFeedbackVal);
+			responseElse.getResponseRules().add(incorrectFeedbackVar);
+		}
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectVelocityRenderDecorator.java b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectVelocityRenderDecorator.java
index ffb53bfd9ef3d6eb4638ff34d9de7a4a9448ecfd..76d55a501c976dee9a324f60d5f3f9ece89dc9d8 100644
--- a/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectVelocityRenderDecorator.java
+++ b/src/main/java/org/olat/ims/qti21/ui/components/AssessmentObjectVelocityRenderDecorator.java
@@ -253,9 +253,10 @@ public class AssessmentObjectVelocityRenderDecorator extends VelocityRenderDecor
 			choices = interaction.getSimpleChoices();
 		}
 		
-		return choices.stream()
+		List<SimpleChoice> visibleChoices = choices.stream()
 			.filter((choice) -> isVisible(choice, itemSessionState))
 			.collect(Collectors.toList());
+		return visibleChoices;
 	}
 	
 	public List<SimpleChoice> getVisibleOrderedSimpleChoices(OrderInteraction interaction) {
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
index 4db2bab27e4e5d804b288b8a4e6ad98fb863f3b1..1104d95de6ea174d29e02565cccb92913ce42488 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/AssessmentItemEditorController.java
@@ -25,7 +25,6 @@ import java.util.List;
 
 import org.olat.core.gui.UserRequest;
 import org.olat.core.gui.components.Component;
-import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.components.tabbedpane.TabbedPane;
 import org.olat.core.gui.components.velocity.VelocityContainer;
 import org.olat.core.gui.control.Controller;
@@ -34,6 +33,8 @@ import org.olat.core.gui.control.WindowControl;
 import org.olat.core.gui.control.controller.BasicController;
 import org.olat.ims.qti21.QTI21Constants;
 import org.olat.ims.qti21.QTI21Service;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
 import org.olat.ims.qti21.ui.AssessmentItemDisplayController;
 import org.olat.modules.assessment.AssessmentEntry;
 import org.olat.modules.assessment.AssessmentService;
@@ -59,7 +60,7 @@ public class AssessmentItemEditorController extends BasicController {
 	private final TabbedPane tabbedPane;
 	private final VelocityContainer mainVC;
 	
-	private FormBasicController itemEditor;
+	private EditorController itemEditor, scoreEditor, feedbackEditor;
 	private AssessmentItemDisplayController displayCtrl;
 	
 	@Autowired
@@ -113,9 +114,7 @@ public class AssessmentItemEditorController extends BasicController {
 			}
 			
 			if(choice && !unkown) {
-				itemEditor = new SingleChoiceEditorController(ureq, getWindowControl());
-				listenTo(itemEditor);
-				tabbedPane.addTab("Choice", itemEditor.getInitialComponent());
+				initSingleChoiceEditors(ureq, item);
 			} else if(unkown) {
 				initItemCreatedByUnkownEditor(ureq);
 			}
@@ -129,6 +128,20 @@ public class AssessmentItemEditorController extends BasicController {
 		listenTo(itemEditor);
 		tabbedPane.addTab("Unkown", itemEditor.getInitialComponent());
 	}
+	
+	private void initSingleChoiceEditors(UserRequest ureq, AssessmentItem item) {
+		SingleChoiceAssessmentItemBuilder itemBuilder = new SingleChoiceAssessmentItemBuilder(item, qtiService.qtiSerializer());
+		itemEditor = new SingleChoiceEditorController(ureq, getWindowControl(), itemBuilder);
+		listenTo(itemEditor);
+		scoreEditor = new SingleChoiceScoreController(ureq, getWindowControl(), itemBuilder);
+		listenTo(scoreEditor);
+		feedbackEditor = new FeedbackEditorController(ureq, getWindowControl(), itemBuilder);
+		listenTo(feedbackEditor);
+		
+		tabbedPane.addTab("Choice", itemEditor.getInitialComponent());
+		tabbedPane.addTab("Score", scoreEditor.getInitialComponent());
+		tabbedPane.addTab("Feedback", feedbackEditor.getInitialComponent());
+	}
 
 	@Override
 	protected void event(UserRequest ureq, Component source, Event event) {
@@ -138,10 +151,16 @@ public class AssessmentItemEditorController extends BasicController {
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
 		if(event instanceof AssessmentItemEvent) {
-			if(event == AssessmentItemEvent.CHANGED_EVENT) {
+			if(event == AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED) {
+				if(source instanceof EditorController) {
+					EditorController editorCtrl = (EditorController)source;
+					AssessmentItemBuilder builder = editorCtrl.getBuilder();
+					if(builder != null) {
+						builder.build();
+					}
+				}
 				doSaveAssessmentItem();
 			}
-			
 		}
 		super.event(ureq, source, event);
 	}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java
new file mode 100644
index 0000000000000000000000000000000000000000..0f761f189ecff1cc130a6207dc98d9c37126d2ca
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/EditorController.java
@@ -0,0 +1,31 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.ui.editor;
+
+import org.olat.core.gui.control.Controller;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+
+public interface EditorController extends Controller {
+	
+	public void updateFromBuilder();
+	
+	public AssessmentItemBuilder getBuilder();
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java
new file mode 100644
index 0000000000000000000000000000000000000000..5673318c3af825627b6c3499914d90a5c7d069fb
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/FeedbackEditorController.java
@@ -0,0 +1,125 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.ui.editor;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.RichTextElement;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.richText.RichTextConfiguration;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.StringHelper;
+import org.olat.core.util.filter.FilterFactory;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.ModalFeedbackBuilder;
+import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+
+/**
+ * 
+ * Initial date: 09.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class FeedbackEditorController extends FormBasicController implements EditorController {
+	
+	private TextElement feedbackCorrectTitleEl, feedbackIncorrectTitleEl;
+	private RichTextElement feedbackCorrectTextEl, feedbackIncorrectTextEl;
+
+	private SingleChoiceAssessmentItemBuilder itemBuilder;
+	
+	public FeedbackEditorController(UserRequest ureq, WindowControl wControl, SingleChoiceAssessmentItemBuilder itemBuilder) {
+		super(ureq, wControl);
+		this.itemBuilder = itemBuilder;
+		initForm(ureq);
+	}
+
+	@Override
+	public void updateFromBuilder() {
+		//
+	}
+
+	@Override
+	public AssessmentItemBuilder getBuilder() {
+		return itemBuilder;
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		//correct feedback
+		ModalFeedbackBuilder correctFeedback = itemBuilder.getCorrectFeedback();
+		String correctTitle = correctFeedback == null ? "" : correctFeedback.getTitle();
+		feedbackCorrectTitleEl = uifactory.addTextElement("correctTitle", "form.imd.correct.title", -1, correctTitle, formLayout);
+		feedbackCorrectTitleEl.setUserObject(correctFeedback);
+		String correctText = correctFeedback == null ? "" : correctFeedback.getText();
+		feedbackCorrectTextEl = uifactory.addRichTextElementForStringData("correctText", "form.imd.correct.text", correctText, 8, -1, true, null, null,
+				formLayout, ureq.getUserSession(), getWindowControl());
+		RichTextConfiguration richTextConfig = feedbackCorrectTextEl.getEditorConfiguration();
+		richTextConfig.setFileBrowserUploadRelPath("media");// set upload dir to the media dir
+
+		//incorrect feedback
+		ModalFeedbackBuilder incorrectFeedback = itemBuilder.getIncorrectFeedback();
+		String incorrectTitle = incorrectFeedback == null ? "" : incorrectFeedback.getTitle();
+		feedbackIncorrectTitleEl = uifactory.addTextElement("incorrectTitle", "form.imd.incorrect.title", -1, incorrectTitle, formLayout);
+		feedbackIncorrectTitleEl.setUserObject(incorrectFeedback);
+		String incorrectText = incorrectFeedback == null ? "" : incorrectFeedback.getText();
+		feedbackIncorrectTextEl = uifactory.addRichTextElementForStringData("incorrectText", "form.imd.incorrect.text", incorrectText, 8, -1, true, null, null,
+				formLayout, ureq.getUserSession(), getWindowControl());
+		RichTextConfiguration richTextConfig2 = feedbackIncorrectTextEl.getEditorConfiguration();
+		richTextConfig2.setFileBrowserUploadRelPath("media");// set upload dir to the media dir
+	
+		// Submit Button
+		FormLayoutContainer buttonsContainer = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		buttonsContainer.setRootForm(mainForm);
+		formLayout.add(buttonsContainer);
+		uifactory.addFormSubmitButton("submit", buttonsContainer);
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		String correctTitle = feedbackCorrectTitleEl.getValue();
+		String correctText = feedbackCorrectTextEl.getValue();
+		if(StringHelper.containsNonWhitespace(FilterFactory.getHtmlTagsFilter().filter(correctText))) {
+			ModalFeedbackBuilder correctBuilder = itemBuilder.getCorrectFeedback();
+			if(correctBuilder == null) {
+				correctBuilder = itemBuilder.createCorrectFeedback();
+			}
+			correctBuilder.setTitle(correctTitle);
+			correctBuilder.setText(correctText);
+		}
+		
+		String incorrectTitle = feedbackIncorrectTitleEl.getValue();
+		String incorrectText = feedbackIncorrectTextEl.getValue();
+		if(StringHelper.containsNonWhitespace(correctTitle)) {
+			ModalFeedbackBuilder incorrectBuilder = (ModalFeedbackBuilder)feedbackIncorrectTitleEl.getUserObject();
+			incorrectBuilder.setTitle(incorrectTitle);
+			incorrectBuilder.setText(incorrectText);
+		}
+
+		fireEvent(ureq, AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED);
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
index 05f848c5b2e7644412bbcde92c176c757e9ea279..c35cdadc940afc84f83ed0a129fc0948cc190ecf 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceEditorController.java
@@ -19,11 +19,35 @@
  */
 package org.olat.ims.qti21.ui.editor;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItem;
 import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.FormLink;
+import org.olat.core.gui.components.form.flexible.elements.RichTextElement;
+import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormEvent;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.components.form.flexible.impl.elements.richText.RichTextConfiguration;
+import org.olat.core.gui.components.link.Link;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.core.util.StringHelper;
+import org.olat.ims.qti21.model.IdentifierGenerator;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.AssessmentItemFactory;
+import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+
+import uk.ac.ed.ph.jqtiplus.node.content.basic.Block;
+import uk.ac.ed.ph.jqtiplus.node.content.basic.FlowStatic;
+import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.ChoiceInteraction;
+import uk.ac.ed.ph.jqtiplus.node.item.interaction.choice.SimpleChoice;
+import uk.ac.ed.ph.jqtiplus.types.Identifier;
 
 /**
  * 
@@ -31,27 +55,306 @@ import org.olat.core.gui.control.WindowControl;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class SingleChoiceEditorController extends FormBasicController {
+public class SingleChoiceEditorController extends FormBasicController implements EditorController {
+
+	private TextElement titleEl;
+	private RichTextElement textEl;
+	private FormLayoutContainer answersCont;
+	private final List<SimpleChoiceWrapper> choiceWrappers = new ArrayList<>();
 	
-	public SingleChoiceEditorController(UserRequest ureq, WindowControl wControl) {
-		super(ureq, wControl);
-		
+	private int count = 0;
+	private final SingleChoiceAssessmentItemBuilder itemBuilder;
+
+	public SingleChoiceEditorController(UserRequest ureq, WindowControl wControl, SingleChoiceAssessmentItemBuilder itemBuilder) {
+		super(ureq, wControl, "simple_choices_editor");
+		this.itemBuilder = itemBuilder;
 		initForm(ureq);
 	}
+	
+	private SingleSelection shuffleEl;
+	
+	private static final String[] yesnoKeys = new String[]{ "y", "n"};
 
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		setFormTitle("editor.sc.title");
-		//
+		
+		FormLayoutContainer metadata = FormLayoutContainer.createDefaultFormLayout("metadata", getTranslator());
+		metadata.setRootForm(mainForm);
+		formLayout.add(metadata);
+		formLayout.add("metadata", metadata);
+
+		titleEl = uifactory.addTextElement("title", "form.imd.title", -1, itemBuilder.getTitle(), metadata);
+		titleEl.setMandatory(true);
+		
+		String description = itemBuilder.getQuestion();
+		textEl = uifactory.addRichTextElementForStringData("desc", "form.imd.descr", description, 8, -1, true, null, null,
+				metadata, ureq.getUserSession(), getWindowControl());
+		RichTextConfiguration richTextConfig = textEl.getEditorConfiguration();
+		richTextConfig.setFileBrowserUploadRelPath("media");// set upload dir to the media dir
+				
+		//points -> in other controller
+		
+		//shuffle
+		String[] yesnoValues = new String[]{ translate("yes"), translate("no") };
+		shuffleEl = uifactory.addRadiosHorizontal("shuffle", "form.imd.shuffle", formLayout, yesnoKeys, yesnoValues);
+		if (itemBuilder.isShuffle()) {
+			shuffleEl.select("y", true);
+		} else {
+			shuffleEl.select("n", true);
+		}
+
+		//responses
+		String page = velocity_root + "/simple_choices.html";
+		answersCont = FormLayoutContainer.createCustomFormLayout("answers", getTranslator(), page);
+		answersCont.setRootForm(mainForm);
+		formLayout.add(answersCont);
+		formLayout.add("answers", answersCont);
+
+		ChoiceInteraction interaction = itemBuilder.getChoiceInteraction();
+		if(interaction != null) {
+			
+			List<SimpleChoice> choices = itemBuilder.getSimpleChoices();
+			for(SimpleChoice choice:choices) {
+				wrapAnswer(ureq, choice);
+			}
+		}
+		answersCont.contextPut("choices", choiceWrappers);
+		recalculateUpDownLinks();
+
+		// Submit Button
+		FormLayoutContainer buttonsContainer = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		buttonsContainer.setRootForm(mainForm);
+		formLayout.add(buttonsContainer);
+		formLayout.add("buttons", buttonsContainer);
+		uifactory.addFormSubmitButton("submit", buttonsContainer);
+	}
+	
+	@Override
+	public void updateFromBuilder() {
+		
 	}
 	
+	@Override
+	public AssessmentItemBuilder getBuilder() {
+		return itemBuilder;
+	}
+
+	private void wrapAnswer(UserRequest ureq, SimpleChoice choice) {
+		String choiceContent =  itemBuilder.getHelper().toString(choice.getFlowStatics());
+		String choiceId = "answer" + count++;
+		RichTextElement choiceEl = uifactory.addRichTextElementForStringData(choiceId, "form.imd.answer", choiceContent, 8, -1, true, null, null,
+				answersCont, ureq.getUserSession(), getWindowControl());
+		choiceEl.setUserObject(choice);
+		answersCont.add("choiceId", choiceEl);
+		
+		FormLink removeLink = uifactory.addFormLink("rm-".concat(choiceId), "rm", "", null, answersCont, Link.NONTRANSLATED);
+		removeLink.setIconLeftCSS("o_icon o_icon-lg o_icon_delete");
+		answersCont.add(removeLink);
+		answersCont.add("rm-".concat(choiceId), removeLink);
+		
+		FormLink addLink = uifactory.addFormLink("add-".concat(choiceId), "add", "", null, answersCont, Link.NONTRANSLATED);
+		addLink.setIconLeftCSS("o_icon o_icon-lg o_icon_add");
+		answersCont.add(addLink);
+		answersCont.add("add-".concat(choiceId), addLink);
+		
+		FormLink upLink = uifactory.addFormLink("up-".concat(choiceId), "up", "", null, answersCont, Link.NONTRANSLATED);
+		upLink.setIconLeftCSS("o_icon o_icon-lg o_icon_move_up");
+		answersCont.add(upLink);
+		answersCont.add("up-".concat(choiceId), upLink);
+		
+		FormLink downLink = uifactory.addFormLink("down-".concat(choiceId), "down", "", null, answersCont, Link.NONTRANSLATED);
+		downLink.setIconLeftCSS("o_icon o_icon-lg o_icon_move_down");
+		answersCont.add(downLink);
+		answersCont.add("down-".concat(choiceId), downLink);
+		
+		boolean correct = itemBuilder.isCorrect(choice);
+		choiceWrappers.add(new SimpleChoiceWrapper(choice, correct, choiceEl, removeLink, addLink, upLink, downLink));
+	}
+
 	@Override
 	protected void doDispose() {
 		//
 	}
 
+	@Override
+	protected boolean validateFormLogic(UserRequest ureq) {
+		boolean allOk = true;
+
+		titleEl.clearError();
+		if(!StringHelper.containsNonWhitespace(titleEl.getValue())) {
+			titleEl.setErrorKey("form.legende.mandatory", null);
+			allOk &= false;
+		}
+		
+		return allOk & super.validateFormLogic(ureq);
+	}
+
 	@Override
 	protected void formOK(UserRequest ureq) {
-		//
+		//title
+		itemBuilder.setTitle(titleEl.getValue());
+		//question
+		String questionText = textEl.getValue();
+		itemBuilder.setQuestion(questionText);
+		
+		//correct response
+		String correctAnswer = ureq.getParameter("correct");
+		Identifier correctAnswerIdentifier = Identifier.parseString(correctAnswer);
+		itemBuilder.setCorrectAnswer(correctAnswerIdentifier);
+		
+		//shuffle
+		itemBuilder.setShuffle(shuffleEl.isOneSelected() && shuffleEl.isSelected(0));
+		
+		//replace simple choices
+		List<SimpleChoice> choiceList = new ArrayList<>();
+		for(SimpleChoiceWrapper choiceWrapper:choiceWrappers) {
+			SimpleChoice choice = choiceWrapper.getSimpleChoice();
+			choiceWrapper.setCorrect(correctAnswerIdentifier.equals(choiceWrapper.getIdentifier()));
+			//text
+			String answer = choiceWrapper.getAnswer().getValue();
+			List<Block> blocks = itemBuilder.getHelper().parseHtml(answer);
+			choice.getFlowStatics().clear();
+			for(Block block:blocks) {
+				if(block instanceof FlowStatic) {
+					choice.getFlowStatics().add((FlowStatic)block);
+				}
+			}
+			
+			choiceList.add(choice);
+		}
+		itemBuilder.setSimpleChoices(choiceList);
+
+		fireEvent(ureq, AssessmentItemEvent.ASSESSMENT_ITEM_CHANGED);
+	}
+	
+	@Override
+	protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
+		if(source instanceof FormLink) {
+			FormLink button = (FormLink)source;
+			String cmd = button.getCmd();
+			if("rm".equals(cmd)) {
+				doRemoveSimpleChoice((SimpleChoiceWrapper)button.getUserObject());
+			} else if("add".equals(cmd)) {
+				doAddSimpleChoice(ureq);
+			} else if("up".equals(cmd)) {
+				doMoveSimpleChoiceUp((SimpleChoiceWrapper)button.getUserObject());
+			} else if("down".equals(cmd)) {
+				doMoveSimpleChoiceDown((SimpleChoiceWrapper)button.getUserObject());
+			}
+		}
+		super.formInnerEvent(ureq, source, event);
+	}
+	
+	private void doAddSimpleChoice(UserRequest ureq) {
+		ChoiceInteraction interaction = itemBuilder.getChoiceInteraction();
+		SimpleChoice newChoice = new SimpleChoice(interaction);
+		newChoice.setIdentifier(IdentifierGenerator.newAsIdentifier());
+		P firstChoiceText = AssessmentItemFactory.getParagraph(newChoice, "New answer");
+		newChoice.getFlowStatics().add(firstChoiceText);
+		
+		wrapAnswer(ureq, newChoice);
+		flc.setDirty(true);
+	}
+	
+	private void doRemoveSimpleChoice(SimpleChoiceWrapper choiceWrapper) {
+		choiceWrappers.remove(choiceWrapper);
+		flc.setDirty(true);
+	}
+	
+	private void doMoveSimpleChoiceUp(SimpleChoiceWrapper choiceWrapper) {
+		int index = choiceWrappers.indexOf(choiceWrapper) - 1;
+		if(index >= 0 && index < choiceWrappers.size()) {
+			choiceWrappers.remove(choiceWrapper);
+			choiceWrappers.add(index, choiceWrapper);
+		}
+		recalculateUpDownLinks();
+		flc.setDirty(true);
+	}
+	
+	private void doMoveSimpleChoiceDown(SimpleChoiceWrapper choiceWrapper) {
+		int index = choiceWrappers.indexOf(choiceWrapper) + 1;
+		if(index > 0 && index < choiceWrappers.size()) {
+			choiceWrappers.remove(choiceWrapper);
+			choiceWrappers.add(index, choiceWrapper);
+		}
+		recalculateUpDownLinks();
+		flc.setDirty(true);
+	}
+	
+	private void recalculateUpDownLinks() {
+		int numOfChoices = choiceWrappers.size();
+		for(int i=0; i<numOfChoices; i++) {
+			SimpleChoiceWrapper choiceWrapper = choiceWrappers.get(i);
+			choiceWrapper.getUp().setEnabled(i != 0);
+			choiceWrapper.getDown().setEnabled(i < (numOfChoices - 1));
+		}
+	}
+
+	public static final class SimpleChoiceWrapper {
+		
+		private final SimpleChoice choice;
+		private final RichTextElement answerEl;
+		private final FormLink removeLink, addLink, upLink, downLink;
+		
+		private boolean correct;
+		private final Identifier choiceIdentifier;
+		
+		public SimpleChoiceWrapper(SimpleChoice choice, boolean correct, RichTextElement answerEl,
+				FormLink removeLink, FormLink addLink, FormLink upLink, FormLink downLink) {
+			this.choice = choice;
+			this.correct = correct;
+			this.choiceIdentifier = choice.getIdentifier();
+			this.answerEl = answerEl;
+			answerEl.setUserObject(this);
+			this.removeLink = removeLink;
+			removeLink.setUserObject(this);
+			this.addLink = addLink;
+			addLink.setUserObject(this);
+			this.upLink = upLink;
+			upLink.setUserObject(this);
+			this.downLink = downLink;
+			downLink.setUserObject(this);
+		}
+		
+		public Identifier getIdentifier() {
+			return choiceIdentifier;
+		}
+		
+		public String getIdentifierString() {
+			return choiceIdentifier.toString();
+		}
+		
+		public boolean isCorrect() {
+			return correct;
+		}
+		
+		public void setCorrect(boolean correct) {
+			this.correct = correct;
+		}
+		
+		public SimpleChoice getSimpleChoice() {
+			return choice;
+		}
+		
+		public RichTextElement getAnswer() {
+			return answerEl;
+		}
+
+		public FormLink getRemove() {
+			return removeLink;
+		}
+
+		public FormLink getAdd() {
+			return addLink;
+		}
+
+		public FormLink getUp() {
+			return upLink;
+		}
+
+		public FormLink getDown() {
+			return downLink;
+		}
 	}
 }
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java
new file mode 100644
index 0000000000000000000000000000000000000000..9c47e8a1582dc7000758476f8e34fdb4891f558e
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/SingleChoiceScoreController.java
@@ -0,0 +1,93 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.ui.editor;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.FormItemContainer;
+import org.olat.core.gui.components.form.flexible.elements.TextElement;
+import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
+import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
+import org.olat.core.gui.control.Controller;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
+import org.olat.ims.qti21.model.xml.ScoreBuilder;
+import org.olat.ims.qti21.model.xml.SingleChoiceAssessmentItemBuilder;
+
+/**
+ * 
+ * Initial date: 08.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class SingleChoiceScoreController extends FormBasicController implements EditorController {
+	
+	private TextElement minScoreEl;
+	private TextElement maxScoreEl;
+	
+	private SingleChoiceAssessmentItemBuilder itemBuilder;
+	
+	public SingleChoiceScoreController(UserRequest ureq, WindowControl wControl, SingleChoiceAssessmentItemBuilder itemBuilder) {
+		super(ureq, wControl);
+		this.itemBuilder = itemBuilder;
+		initForm(ureq);
+	}
+
+	@Override
+	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
+		minScoreEl = uifactory.addTextElement("min.score", "min.score", 8, "0.0", formLayout);
+		minScoreEl.setEnabled(false);
+		
+		ScoreBuilder maxScore = itemBuilder.getMaxScoreBuilder();
+		String maxValue = maxScore == null ? "" : (maxScore.getScore() == null ? "" : maxScore.getScore().toString());
+		maxScoreEl = uifactory.addTextElement("max.score", "max.score", 8, maxValue, formLayout);
+		
+		// Submit Button
+		FormLayoutContainer buttonsContainer = FormLayoutContainer.createButtonLayout("buttons", getTranslator());
+		buttonsContainer.setRootForm(mainForm);
+		formLayout.add(buttonsContainer);
+		uifactory.addFormSubmitButton("submit", buttonsContainer);
+	}
+	
+	@Override
+	public void updateFromBuilder() {
+		
+	}
+	
+	@Override
+	public AssessmentItemBuilder getBuilder() {
+		return itemBuilder;
+	}
+
+	@Override
+	protected void formOK(UserRequest ureq) {
+		String maxScoreValue = maxScoreEl.getValue();
+		Double maxScore = Double.parseDouble(maxScoreValue);
+		itemBuilder.setMaxScore(maxScore);
+		itemBuilder.setMinScore(new Double(0d));
+	}
+
+	@Override
+	protected void doDispose() {
+		//
+	}
+	
+	
+
+}
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
index c44c0bf8cea77c6d3b4f5cb876f1d511537a16ae..9184c7a241e0905cb1d9d5b523ab80a18dd223a4 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/UnkownItemEditorController.java
@@ -24,6 +24,7 @@ import org.olat.core.gui.components.form.flexible.FormItemContainer;
 import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
 import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
+import org.olat.ims.qti21.model.xml.AssessmentItemBuilder;
 
 /**
  * 
@@ -31,7 +32,7 @@ import org.olat.core.gui.control.WindowControl;
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
-public class UnkownItemEditorController extends FormBasicController {
+public class UnkownItemEditorController extends FormBasicController implements EditorController {
 	
 	public UnkownItemEditorController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl);
@@ -45,6 +46,16 @@ public class UnkownItemEditorController extends FormBasicController {
 		//
 	}
 	
+	@Override
+	public void updateFromBuilder() {
+		//
+	}
+
+	@Override
+	public AssessmentItemBuilder getBuilder() {
+		return null;
+	}
+
 	@Override
 	protected void doDispose() {
 		//
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices.html b/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices.html
new file mode 100644
index 0000000000000000000000000000000000000000..73e244176f545d4a8ff6674eefb6e7ad7b72e833
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices.html
@@ -0,0 +1,44 @@
+<fieldset class="o_form">
+
+<div class="form-group #if($f.hasError($item)) has-feedback has-error #end clearfix">
+	<div class="col-sm-2"><strong>[Correct]</strong></div>
+	<div class="col-sm-8"><strong>[Answer]</strong></div>
+	<div class="col-sm-2"><strong>[Actions]</strong></div>
+</div>
+
+#foreach($choice in $choices)
+
+<div class="form-group #if($f.hasError($item)) has-feedback has-error #end clearfix">
+	<div class="col-sm-2">
+		<input type="radio" name="correct" value="${choice.getIdentifierString()}" #if(${choice.isCorrect()}) checked #end/>
+	</div>
+	<div class="col-sm-8">
+		$r.render(${choice.getAnswer().getComponent().getComponentName()})
+		#if($f.hasError($item))
+			<span class="o_icon o_icon_error form-control-feedback"></span>
+		#end
+	</div>
+	<div class="col-sm-2">
+		#if($r.available(${choice.getRemove().getComponent().getComponentName()}) && $r.visible(${choice.getRemove().getComponent().getComponentName()}))
+			$r.render(${choice.getRemove().getComponent().getComponentName()})
+		#end
+		#if($r.available(${choice.getAdd().getComponent().getComponentName()}) && $r.visible(${choice.getAdd().getComponent().getComponentName()}))
+			$r.render(${choice.getAdd().getComponent().getComponentName()})
+		#end
+		#if($r.available(${choice.getUp().getComponent().getComponentName()}) && $r.visible(${choice.getUp().getComponent().getComponentName()}))
+			$r.render(${choice.getUp().getComponent().getComponentName()})
+		#end
+		#if($r.available(${choice.getDown().getComponent().getComponentName()}) && $r.visible(${choice.getDown().getComponent().getComponentName()}))
+			$r.render(${choice.getDown().getComponent().getComponentName()})
+		#end
+	</div>
+
+	#if($f.hasError($item))
+		<div class="col-sm-offset-2 col-sm-8">
+			$r.render("${item}_ERROR")
+		</div>
+	#end
+</div>
+
+#end
+</fieldset>
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices_editor.html b/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices_editor.html
new file mode 100644
index 0000000000000000000000000000000000000000..5245149a4f1b36645b6faa6ae16825dba831d807
--- /dev/null
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_content/simple_choices_editor.html
@@ -0,0 +1,3 @@
+$r.render("metadata")
+$r.render("answers")
+$r.render("buttons")
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
index fc8aa54fd70ef73f3105a9ec0cc39e51a2382441..88cd94609c457d55e0bd694399d70ada03acb762 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_de.properties
@@ -1,3 +1 @@
 #Mon Mar 02 09:54:04 CET 2009
-editor.sc.title=Single choice
-editor.unkown.title=Unkown interaction
\ No newline at end of file
diff --git a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
index d186f30e90630ec062d32dfc5d60d44720643075..f38be912ca9aa039e8a94ee58c28d87bc7117c4d 100644
--- a/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/ims/qti21/ui/editor/_i18n/LocalStrings_en.properties
@@ -1,3 +1,14 @@
 #Sat Jan 22 17:01:28 CET 2011
 editor.sc.title=Single choice
-editor.unkown.title=Unkown interaction
\ No newline at end of file
+editor.unkown.title=Unkown interaction
+form.imd.title=Titel
+form.imd.descr=Frage
+form.imd.answer=Antwort
+form.imd.layout.horizontal=Horizontal
+form.imd.layout.vertical=Vertical
+form.imd.correct.title=Correct title
+form.imd.incorrect.title=Incorrect title
+form.imd.correct.text=Correct feedback
+form.imd.incorrect.text=Incorrect feedback
+min.score=Min. score
+max.score=Max. score
\ No newline at end of file
diff --git a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
index a93eafac80fbd96ce1fe9785d8fc4c3a4213d090..10002985fe28b9baffa36b33fa63f78b39683603 100644
--- a/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
+++ b/src/main/java/org/olat/upgrade/OLATUpgrade_11_0_0.java
@@ -298,7 +298,7 @@ public class OLATUpgrade_11_0_0 extends OLATUpgrade {
 					int nodeIdentIndex = propertyCategory.indexOf("::");
 					if(nodeIdentIndex > 0) {
 						String nodeIdent = propertyCategory.substring(propertyCategory.indexOf("::") + 2);
-						AssessmentDataKey key = new AssessmentDataKey(property.getIdentity(), property.getResourceTypeId(), nodeIdent);
+						AssessmentDataKey key = new AssessmentDataKey(property.getIdentity().getKey(), property.getResourceTypeId(), nodeIdent);
 						if(curentNodeAssessmentMap.containsKey(key)) {
 							continue;
 						}
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilderTest.java b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..2889dd2c80aa7ddc2638f699f1ffe4c0a6b17b39
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/model/xml/AssessmentItemBuilderTest.java
@@ -0,0 +1,67 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Paths;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.olat.fileresource.types.ImsQTI21Resource.PathResourceLocator;
+
+import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.node.item.AssessmentItem;
+import uk.ac.ed.ph.jqtiplus.reading.AssessmentObjectXmlLoader;
+import uk.ac.ed.ph.jqtiplus.reading.QtiXmlReader;
+import uk.ac.ed.ph.jqtiplus.resolution.ResolvedAssessmentItem;
+import uk.ac.ed.ph.jqtiplus.serialization.QtiSerializer;
+import uk.ac.ed.ph.jqtiplus.xmlutils.locators.ResourceLocator;
+
+public class AssessmentItemBuilderTest {
+	
+	private final JqtiExtensionManager jqtiExtensionManager = new JqtiExtensionManager();
+	private final QtiXmlReader qtiXmlReader = new QtiXmlReader(jqtiExtensionManager);
+	private final QtiSerializer qtiSerializer = new QtiSerializer(jqtiExtensionManager);
+	
+	@Test
+	public void findFeedbacks()  throws URISyntaxException {
+		URL itemUrl = AssessmentItemPackageTest.class.getResource("assessment-item-single-choice-feedbacks.xml");
+		AssessmentItem assessmentItem = loadAssessmentItem(itemUrl);
+		SingleChoiceAssessmentItemBuilder itemBuilder = new SingleChoiceAssessmentItemBuilder(assessmentItem, qtiSerializer);
+		
+		ModalFeedbackBuilder correctFeedback = itemBuilder.getCorrectFeedback();
+		Assert.assertNotNull(correctFeedback);
+		Assert.assertTrue(correctFeedback.isCorrectRule());
+		
+		ModalFeedbackBuilder incorrectFeedback = itemBuilder.getIncorrectFeedback();
+		Assert.assertNotNull(incorrectFeedback);
+		Assert.assertTrue(incorrectFeedback.isIncorrectRule());
+		
+	}
+	
+	private AssessmentItem loadAssessmentItem(URL itemUrl) throws URISyntaxException {
+		ResourceLocator fileResourceLocator = new PathResourceLocator(Paths.get(itemUrl.toURI()));
+        AssessmentObjectXmlLoader assessmentObjectXmlLoader = new AssessmentObjectXmlLoader(qtiXmlReader, fileResourceLocator);
+        ResolvedAssessmentItem item = assessmentObjectXmlLoader.loadAndResolveAssessmentItem(itemUrl.toURI());
+		return item.getItemLookup().getRootNodeHolder().getRootNode();
+	}
+
+}
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java b/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..15138d0b51c7543e7756ba905f5e28eb4a8a2f6b
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/model/xml/ConvertHTMLTestTest.java
@@ -0,0 +1,138 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.ims.qti21.model.xml;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.cyberneko.html.parsers.DOMParser;
+import org.jcodec.common.Assert;
+import org.junit.Test;
+import org.olat.core.util.filter.FilterFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import uk.ac.ed.ph.jqtiplus.JqtiExtensionManager;
+import uk.ac.ed.ph.jqtiplus.exception.QtiModelException;
+import uk.ac.ed.ph.jqtiplus.node.LoadingContext;
+import uk.ac.ed.ph.jqtiplus.node.QtiNode;
+import uk.ac.ed.ph.jqtiplus.node.content.ItemBody;
+import uk.ac.ed.ph.jqtiplus.node.content.xhtml.text.P;
+
+/**
+ * 
+ * Initial date: 07.12.2015<br>
+ * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
+ *
+ */
+public class ConvertHTMLTestTest {
+	
+	@Test
+	public void convert() {
+		String content = "<html><p>Test</p><p>Test 2</p></html>";
+		Document partialDocument = getDoc(content);
+		Element rootElement = partialDocument.getDocumentElement();
+	
+
+		
+		MyLoadingContext context = new MyLoadingContext();
+		
+		Element paragraphEl = (Element)rootElement.getFirstChild();
+		String tagName = paragraphEl.getTagName();
+		String localName = paragraphEl.getLocalName();
+
+		ItemBody itemBody = new ItemBody(null);
+		itemBody.load(rootElement, context);
+		
+		Assert.assertNotNull(localName);
+		Assert.assertEquals(2, itemBody.getBlocks().size());
+
+		
+		
+	}
+	
+	private Document getDoc(String content) {
+		try {
+			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+			factory.setValidating(false);
+			factory.setNamespaceAware(true);
+			DocumentBuilder builder = factory.newDocumentBuilder();
+			Document doc = builder.parse(new ByteArrayInputStream(content.getBytes()));
+			return doc;
+		} catch (ParserConfigurationException | SAXException | IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	private Document getDocument(String content) {
+		try {
+			DOMParser parser = new DOMParser();
+	        parser.setFeature("http://xml.org/sax/features/validation", false);
+	       
+			parser.setFeature( "http://cyberneko.org/html/features/override-namespaces", true);
+			parser.setFeature ( "http://xml.org/sax/features/namespaces", true );
+			parser.setProperty("http://cyberneko.org/html/properties/names/elems", "upper" ); // has no effect, cannot override xerces configuration
+			parser.setProperty( "http://cyberneko.org/html/properties/names/attrs", "upper" ); // has no effect, cannot override xerces configuration
+			parser.setFeature("http://cyberneko.org/html/features/balance-tags/document-fragment",true);
+			parser.parse(new InputSource(new StringReader(content)));
+			return parser.getDocument();
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+	}
+	
+	
+	private List<P> getText(QtiNode choice, String htmlContent) {
+		String text = FilterFactory.getHtmlTagsFilter().filter(htmlContent);
+		P firstChoiceText = AssessmentItemFactory.getParagraph(choice, text);
+		List<P> blocks = new ArrayList<>();
+		blocks.add(firstChoiceText);
+		return blocks;
+	}
+	
+	private static final class MyLoadingContext implements LoadingContext {
+
+		@Override
+		public JqtiExtensionManager getJqtiExtensionManager() {
+			//
+			return null;
+		}
+
+		@Override
+		public void modelBuildingError(QtiModelException exception, Node badNode) {
+			//
+		}
+		
+	}
+
+}
diff --git a/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice-feedbacks.xml b/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice-feedbacks.xml
new file mode 100644
index 0000000000000000000000000000000000000000..7e93179da0b41368f554fdceaa151b176a5a4d3d
--- /dev/null
+++ b/src/test/java/org/olat/ims/qti21/model/xml/assessment-item-single-choice-feedbacks.xml
@@ -0,0 +1,228 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<assessmentItem xmlns="http://www.imsglobal.org/xsd/imsqti_v2p1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.imsglobal.org/xsd/imsqti_v2p1 http://www.imsglobal.org/xsd/qti/qtiv2p1/imsqti_v2p1p1.xsd http://www.w3.org/1998/Math/MathML http://www.w3.org/Math/XMLSchema/mathml2/mathml2.xsd" identifier="id19a005ee-9a8c-4335-a0dd-adca2f8b97b4" title="New question" adaptive="false" timeDependent="false">
+	<responseDeclaration identifier="RESPONSE_1" cardinality="single" baseType="identifier">
+		<correctResponse>
+			<value>
+				id87d42b76-93d7-42fc-bdec-3e2419fa901d
+			</value>
+		</correctResponse>
+	</responseDeclaration>
+	<outcomeDeclaration identifier="SCORE" cardinality="single" baseType="float">
+		<defaultValue>
+			<value>
+				0
+			</value>
+		</defaultValue>
+	</outcomeDeclaration>
+	<outcomeDeclaration identifier="MAXSCORE" cardinality="single" baseType="float">
+		<defaultValue>
+			<value>
+				1
+			</value>
+		</defaultValue>
+	</outcomeDeclaration>
+	<outcomeDeclaration identifier="FEEDBACKBASIC" cardinality="single" baseType="identifier">
+		<defaultValue>
+			<value>
+				empty
+			</value>
+		</defaultValue>
+	</outcomeDeclaration>
+	<outcomeDeclaration identifier="MINSCORE" cardinality="single" baseType="float" view="testConstructor">
+		<defaultValue>
+			<value>
+				0
+			</value>
+		</defaultValue>
+	</outcomeDeclaration>
+	<outcomeDeclaration identifier="FEEDBACKMODAL" cardinality="multiple" baseType="identifier" view="testConstructor" /><templateDeclaration identifier="NEW_VARIABLE" cardinality="single" baseType="integer" />
+	<templateProcessing>
+		<setTemplateValue identifier="NEW_VARIABLE">
+			<randomInteger min="1" max="10" />
+		</setTemplateValue>
+		<templateCondition>
+			<templateIf>
+				<gte>
+					<variable identifier="NEW_VARIABLE" />
+					<baseValue baseType="integer">
+						0
+					</baseValue>
+				</gte>
+				<setTemplateValue identifier="NEW_VARIABLE">
+					<randomInteger min="1" max="10" />
+				</setTemplateValue>
+			</templateIf>
+		</templateCondition>
+	</templateProcessing>
+	<itemBody>
+		<choiceInteraction responseIdentifier="RESPONSE_1" shuffle="true" maxChoices="1">
+			<simpleChoice identifier="id87d42b76-93d7-42fc-bdec-3e2419fa901d">
+				<p>
+					New answer
+				</p>
+			</simpleChoice>
+		</choiceInteraction>
+	</itemBody>
+	<responseProcessing>
+		<responseCondition>
+			<responseIf>
+				<isNull>
+					<variable identifier="RESPONSE_1" />
+				</isNull>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						empty
+					</baseValue>
+				</setOutcomeValue>
+			</responseIf>
+			<responseElseIf>
+				<match>
+					<variable identifier="RESPONSE_1" /><correct identifier="RESPONSE_1" />
+				</match>
+				<setOutcomeValue identifier="SCORE">
+					<sum>
+						<variable identifier="SCORE" /><variable identifier="MAXSCORE" />
+					</sum>
+				</setOutcomeValue>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						correct
+					</baseValue>
+				</setOutcomeValue>
+			</responseElseIf>
+			<responseElse>
+				<setOutcomeValue identifier="FEEDBACKBASIC">
+					<baseValue baseType="identifier">
+						incorrect
+					</baseValue>
+				</setOutcomeValue>
+			</responseElse>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<and>
+					<match>
+						<baseValue baseType="identifier">
+							correct
+						</baseValue>
+						<variable identifier="FEEDBACKBASIC" />
+					</match>
+				</and>
+				<setOutcomeValue identifier="FEEDBACKMODAL">
+					<multiple>
+						<variable identifier="FEEDBACKMODAL" />
+						<baseValue baseType="identifier">
+							Feedback261171147
+						</baseValue>
+					</multiple>
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<and>
+					<match>
+						<baseValue baseType="identifier">
+							incorrect
+						</baseValue>
+						<variable identifier="FEEDBACKBASIC" />
+					</match>
+				</and>
+				<setOutcomeValue identifier="FEEDBACKMODAL">
+					<multiple>
+						<variable identifier="FEEDBACKMODAL" />
+						<baseValue baseType="identifier">
+							Feedback730653886
+						</baseValue>
+					</multiple>
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<and>
+					<equal toleranceMode="exact">
+						<variable identifier="SCORE" />
+						<baseValue baseType="float">
+							0
+						</baseValue>
+					</equal>
+				</and>
+				<setOutcomeValue identifier="FEEDBACKMODAL">
+					<multiple>
+						<variable identifier="FEEDBACKMODAL" />
+						<baseValue baseType="identifier">
+							Feedback560425559
+						</baseValue>
+					</multiple>
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<and>
+					<equal toleranceMode="exact">
+						<variable identifier="SCORE" />
+						<baseValue baseType="float">
+							0
+						</baseValue>
+					</equal>
+					<match>
+						<baseValue baseType="identifier">
+							id87d42b76-93d7-42fc-bdec-3e2419fa901d
+						</baseValue>
+						<variable identifier="RESPONSE_1" />
+					</match>
+				</and>
+				<setOutcomeValue identifier="FEEDBACKMODAL">
+					<multiple>
+						<variable identifier="FEEDBACKMODAL" />
+						<baseValue baseType="identifier">
+							Feedback2007127083
+						</baseValue>
+					</multiple>
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<gt>
+					<variable identifier="SCORE" /><variable identifier="MAXSCORE" />
+				</gt>
+				<setOutcomeValue identifier="SCORE">
+					<variable identifier="MAXSCORE" />
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+		<responseCondition>
+			<responseIf>
+				<lt>
+					<variable identifier="SCORE" /><variable identifier="MINSCORE" />
+				</lt>
+				<setOutcomeValue identifier="SCORE">
+					<variable identifier="MINSCORE" />
+				</setOutcomeValue>
+			</responseIf>
+		</responseCondition>
+	</responseProcessing>
+	<modalFeedback identifier="Feedback261171147" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="Correct answer">
+		<p>
+			This is the correct answer
+		</p>
+	</modalFeedback>
+	<modalFeedback identifier="Feedback730653886" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="Wong">
+		<p>
+			This is the wrong answer
+		</p>
+	</modalFeedback>
+	<modalFeedback identifier="Feedback560425559" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="Answer specific">
+		<p>
+			Answer specific feedback
+		</p>
+	</modalFeedback>
+	<modalFeedback identifier="Feedback2007127083" outcomeIdentifier="FEEDBACKMODAL" showHide="show" title="More specific feedback">
+		<p>
+			Very specific feedback
+		</p>
+	</modalFeedback>
+</assessmentItem>