@Override
  protected void validateThis(
      final ValidationContext context, final ResponseDeclaration responseDeclaration) {
    final int maxChoices = getMaxChoices();
    final int minChoices = getMinChoices();

    if (maxChoices != 0 && minChoices > maxChoices) {
      context.fireValidationError(
          this, "Minimum number of choices can't be bigger than maximum number");
    }

    if (responseDeclaration != null) {
      if (responseDeclaration.getBaseType() != null
          && !responseDeclaration.getBaseType().isIdentifier()) {
        context.fireValidationError(this, "Response variable must have identifier base type");
      }

      if (getMaxChoices() == 1
          && !responseDeclaration.getCardinality().isSingle()
          && !responseDeclaration.getCardinality().isMultiple()) {
        context.fireValidationError(
            this, "Response variable must have single or multiple cardinality");
      }

      if (getMaxChoices() != 1 && !responseDeclaration.getCardinality().isMultiple()) {
        context.fireValidationError(this, "Response variable must have multiple cardinality");
      }
    }
  }
  @Override
  public void validateThis(
      final ValidationContext context, final ResponseDeclaration responseDeclaration) {
    final Integer minChoices = getMinChoices();
    final Integer maxChoices = getMaxChoices();

    if (minChoices != null && minChoices.intValue() < 1) {
      context.fireValidationError(this, "Minimum number of choices can't be less than one");
    }

    if (maxChoices != null && minChoices != null && maxChoices.intValue() < minChoices.intValue()) {
      context.fireValidationError(
          this, "Maximum number of choices must be greater or equal to minimum number of choices");
    }

    if (maxChoices != null && maxChoices.intValue() > getHotspotChoices().size()) {
      context.fireValidationError(
          this, "Maximum number of choices cannot be larger than the number of choice children");
    }

    if (responseDeclaration != null) {
      if (responseDeclaration.getBaseType() != null
          && !responseDeclaration.getBaseType().isIdentifier()) {
        context.fireValidationError(this, "Response variable must have identifier base type");
      }

      if (!responseDeclaration.getCardinality().isOrdered()) {
        context.fireValidationError(this, "Response variable must have ordered cardinality");
      }
    }
  }
 /**
  * Gets (first) explicitly-defined responseDeclaration with given identifier, or null if no such
  * variable is defined.
  *
  * <p>NB: Doesn't include the implicitly-defined {@link QtiConstants#VARIABLE_DURATION_NAME} and
  * {@link QtiConstants#VARIABLE_NUMBER_OF_ATTEMPTS_NAME} variables.
  *
  * @param identifier given identifier
  * @return responseDeclaration with given identifier or null
  */
 public ResponseDeclaration getResponseDeclaration(final Identifier identifier) {
   Assert.notNull(identifier);
   for (final ResponseDeclaration declaration : getResponseDeclarations()) {
     if (identifier.equals(declaration.getIdentifier())) {
       return declaration;
     }
   }
   return null;
 }
  @Override
  public void extract() {
    super.extract();

    if (choiceInteraction != null) {
      ResponseDeclaration responseDeclaration =
          assessmentItem.getResponseDeclaration(choiceInteraction.getResponseIdentifier());
      if (responseDeclaration != null) {
        CorrectResponse correctResponse = responseDeclaration.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 AssessmentItem() {
    super(null, QTI_CLASS_NAME); // Item doesn't have any parent.

    getAttributes().add(new StringAttribute(this, IdentifiableNode.ATTR_IDENTIFIER_NAME, true));

    getAttributes().add(new StringAttribute(this, ATTR_TITLE_NAME, true));
    getAttributes().add(new StringAttribute(this, ATTR_LABEL_NAME, false));
    getAttributes()
        .add(new StringAttribute(this, ATTR_LANG_NAME, XMLConstants.XML_NS_URI, null, false));
    getAttributes().add(new BooleanAttribute(this, ATTR_ADAPTIVE_NAME, true));
    getAttributes().add(new BooleanAttribute(this, ATTR_TIME_DEPENDENT_NAME, true));
    getAttributes().add(new StringAttribute(this, ATTR_TOOL_NAME_NAME, false));
    getAttributes().add(new StringAttribute(this, ATTR_TOOL_VERSION_NAME, false));

    getNodeGroups().add(new ResponseDeclarationGroup(this));
    getNodeGroups().add(new OutcomeDeclarationGroup(this));
    getNodeGroups().add(new TemplateDeclarationGroup(this));

    getNodeGroups().add(new TemplateProcessingGroup(this)); // templateProcessing [0..1]
    getNodeGroups().add(new StylesheetGroup(this)); // stylesheet [0..*]
    getNodeGroups().add(new ItemBodyGroup(this)); // itemBody [0..1]
    getNodeGroups().add(new ResponseProcessingGroup(this)); // responseProcessing [0..1]
    getNodeGroups().add(new ModalFeedbackGroup(this)); // modalFeedback [*]
    getNodeGroups().add(new ApipAccessibilityGroup(this, false));

    /* create a special declaration for the internal completionStatus variable */
    completionStatusOutcomeDeclaration = new OutcomeDeclaration(this);
    completionStatusOutcomeDeclaration.setIdentifier(
        QtiConstants.VARIABLE_COMPLETION_STATUS_IDENTIFIER);
    completionStatusOutcomeDeclaration.setCardinality(Cardinality.SINGLE);
    completionStatusOutcomeDeclaration.setBaseType(BaseType.IDENTIFIER);

    /* create a special declaration for the internal numAttempts variable */
    numAttemptsResponseDeclaration = new ResponseDeclaration(this);
    numAttemptsResponseDeclaration.setIdentifier(
        QtiConstants.VARIABLE_NUMBER_OF_ATTEMPTS_IDENTIFIER);
    numAttemptsResponseDeclaration.setCardinality(Cardinality.SINGLE);
    numAttemptsResponseDeclaration.setBaseType(BaseType.INTEGER);

    /* create a special declaration for the internal duration variable */
    durationResponseDeclaration = new ResponseDeclaration(this);
    durationResponseDeclaration.setIdentifier(QtiConstants.VARIABLE_DURATION_IDENTIFIER);
    durationResponseDeclaration.setCardinality(Cardinality.SINGLE);
    durationResponseDeclaration.setBaseType(BaseType.FLOAT);
  }