private void validateGroup(
      List<ValidationMessage> theErrors,
      GroupComponent theQuestGroup,
      org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup,
      LinkedList<String> thePathStack,
      QuestionnaireResponse theAnswers,
      boolean theValidateRequired) {

    for (org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent next :
        theAnsGroup.getQuestion()) {
      rule(
          theErrors,
          IssueType.INVALID,
          thePathStack,
          isNotBlank(next.getLinkId()),
          "Question found with no linkId");
    }

    Set<String> allowedQuestions = new HashSet<String>();
    for (QuestionComponent nextQuestion : theQuestGroup.getQuestion()) {
      allowedQuestions.add(nextQuestion.getLinkId());
    }

    for (int i = 0; i < theQuestGroup.getQuestion().size(); i++) {
      QuestionComponent nextQuestion = theQuestGroup.getQuestion().get(i);
      validateQuestion(
          theErrors, nextQuestion, theAnsGroup, thePathStack, theAnswers, theValidateRequired);
    }

    // Check that there are no extra answers
    for (int i = 0; i < theAnsGroup.getQuestion().size(); i++) {
      org.hl7.fhir.instance.model.QuestionnaireResponse.QuestionComponent nextQuestion =
          theAnsGroup.getQuestion().get(i);
      thePathStack.add("question[" + i + "]");
      rule(
          theErrors,
          IssueType.BUSINESSRULE,
          thePathStack,
          allowedQuestions.contains(nextQuestion.getLinkId()),
          "Found answer with linkId[{0}] but this ID is not allowed at this position",
          nextQuestion.getLinkId());
      thePathStack.remove();
    }

    validateGroupGroups(
        theErrors, theQuestGroup, theAnsGroup, thePathStack, theAnswers, theValidateRequired);
  }
 private void validateGroupGroups(
     List<ValidationMessage> theErrors,
     GroupComponent theQuestGroup,
     org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent theAnsGroup,
     LinkedList<String> thePathSpec,
     QuestionnaireResponse theAnswers,
     boolean theValidateRequired) {
   validateGroups(
       theErrors,
       theQuestGroup.getGroup(),
       theAnsGroup.getGroup(),
       thePathSpec,
       theAnswers,
       theValidateRequired);
 }
  private void validateGroups(
      List<ValidationMessage> theErrors,
      List<GroupComponent> theQuestionGroups,
      List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> theAnswerGroups,
      LinkedList<String> thePathStack,
      QuestionnaireResponse theAnswers,
      boolean theValidateRequired) {
    Set<String> linkIds = new HashSet<String>();
    for (GroupComponent nextQuestionGroup : theQuestionGroups) {
      String nextLinkId = StringUtils.defaultString(nextQuestionGroup.getLinkId());
      if (!linkIds.add(nextLinkId)) {
        if (isBlank(nextLinkId)) {
          fail(
              theErrors,
              IssueType.BUSINESSRULE,
              thePathStack,
              false,
              "Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with blank/missing linkId",
              nextLinkId);
        } else {
          fail(
              theErrors,
              IssueType.BUSINESSRULE,
              thePathStack,
              false,
              "Questionnaire in invalid, unable to validate QuestionnaireResponse: Multiple groups found at this position with linkId[{0}]",
              nextLinkId);
        }
      }
    }

    Set<String> allowedGroups = new HashSet<String>();
    for (GroupComponent nextQuestionGroup : theQuestionGroups) {
      String linkId = nextQuestionGroup.getLinkId();
      allowedGroups.add(linkId);

      List<org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent> answerGroups =
          findGroupByLinkId(theAnswerGroups, linkId);
      if (answerGroups.isEmpty()) {
        if (nextQuestionGroup.getRequired()) {
          if (theValidateRequired) {
            rule(
                theErrors,
                IssueType.BUSINESSRULE,
                thePathStack,
                false,
                "Missing required group with linkId[{0}]",
                linkId);
          } else {
            hint(
                theErrors,
                IssueType.BUSINESSRULE,
                thePathStack,
                false,
                "Missing required group with linkId[{0}]",
                linkId);
          }
        }
        continue;
      }
      if (answerGroups.size() > 1) {
        if (nextQuestionGroup.getRepeats() == false) {
          int index = theAnswerGroups.indexOf(answerGroups.get(1));
          thePathStack.add("group[" + index + "]");
          rule(
              theErrors,
              IssueType.BUSINESSRULE,
              thePathStack,
              false,
              "Multiple repetitions of group with linkId[{0}] found at this position, but this group can not repeat",
              linkId);
          thePathStack.removeLast();
        }
      }
      for (org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent nextAnswerGroup :
          answerGroups) {
        int index = theAnswerGroups.indexOf(nextAnswerGroup);
        thePathStack.add("group[" + index + "]");
        validateGroup(
            theErrors,
            nextQuestionGroup,
            nextAnswerGroup,
            thePathStack,
            theAnswers,
            theValidateRequired);
        thePathStack.removeLast();
      }
    }

    // Make sure there are no groups in answers that aren't in the questionnaire
    int idx = -1;
    for (org.hl7.fhir.instance.model.QuestionnaireResponse.GroupComponent next : theAnswerGroups) {
      idx++;
      if (!allowedGroups.contains(next.getLinkId())) {
        thePathStack.add("group[" + idx + "]");
        rule(
            theErrors,
            IssueType.BUSINESSRULE,
            thePathStack,
            false,
            "Group with linkId[{0}] found at this position, but this group does not exist at this position in Questionnaire",
            next.getLinkId());
        thePathStack.removeLast();
      }
    }
  }