@Override
  public void checkThisTemplate(
      final CompilationTimeStamp timestamp,
      final ITTCN3Template template,
      final boolean isModified,
      final boolean implicitOmit) {
    registerUsage(template);
    template.setMyGovernor(this);

    switch (template.getTemplatetype()) {
      case OMIT_VALUE:
        if (template.getLengthRestriction() != null) {
          template.getLocation().reportSemanticWarning(REDUNDANTLENGTHRESTRICTION);
        }
        break;
      case PERMUTATION_MATCH:
        {
          PermutationMatch_Template permutationTemplate = (PermutationMatch_Template) template;
          int nofComponents = permutationTemplate.getNofTemplates();
          for (int i = 0; i < nofComponents; i++) {
            ITTCN3Template templateComponent =
                permutationTemplate.getTemplateByIndex(
                    i); // FIXME: type is ok? It should be ITemplateListItem!
            templateComponent.setMyGovernor(getOfType());
            templateComponent =
                getOfType()
                    .checkThisTemplateRef(
                        timestamp,
                        templateComponent); // It does not do anything for AllElementsFrom, it is ok
            templateComponent.checkThisTemplateGeneric(
                timestamp,
                getOfType(),
                false,
                false,
                true,
                true,
                implicitOmit); // it is a special for AllElementsFrom, it is the usual for
                               // TemplateBody
          }
          break;
        }
      case SUPERSET_MATCH:
        {
          SupersetMatch_Template supersetTemplate = (SupersetMatch_Template) template;
          int nofComponents = supersetTemplate.getNofTemplates();
          for (int i = 0; i < nofComponents; i++) {
            ITTCN3Template templateComponent =
                supersetTemplate.getTemplateByIndex(
                    i); // FIXME: type is ok? It should be ITemplateListItem!
            templateComponent.setMyGovernor(getOfType());
            templateComponent =
                getOfType()
                    .checkThisTemplateRef(
                        timestamp,
                        templateComponent); // It does not do anything for AllElementsFrom, it is ok
            templateComponent.checkThisTemplateGeneric(
                timestamp,
                getOfType(),
                false,
                false,
                true,
                true,
                implicitOmit); // it is a special for AllElementsFrom, it is the usual for
                               // TemplateBody
          }
          break;
        }
      case SUBSET_MATCH:
        {
          SubsetMatch_Template subsetTemplate = (SubsetMatch_Template) template;
          int nofComponents = subsetTemplate.getNofTemplates();
          for (int i = 0; i < nofComponents; i++) {
            ITTCN3Template templateComponent =
                subsetTemplate.getTemplateByIndex(
                    i); // FIXME: type is ok? It should be ITemplateListItem!
            templateComponent.setMyGovernor(getOfType());
            templateComponent =
                getOfType()
                    .checkThisTemplateRef(
                        timestamp,
                        templateComponent); // It does not do anything for AllElementsFrom, it is ok
            templateComponent.checkThisTemplateGeneric(
                timestamp,
                getOfType(),
                false,
                false,
                true,
                true,
                implicitOmit); // it is a special for AllElementsFrom, it is the usual for
                               // TemplateBody
          }
          break;
        }
      case TEMPLATE_LIST:
        {
          Completeness_type completeness =
              template.getCompletenessConditionSeof(timestamp, isModified);
          Template_List base = null;
          int nofBaseComps = 0;
          if (Completeness_type.PARTIAL.equals(completeness)) {
            ITTCN3Template tempBase = template.getBaseTemplate();
            if (tempBase != null) {
              tempBase = tempBase.getTemplateReferencedLast(timestamp);
            }

            if (tempBase == null) {
              setIsErroneous(true);
              return;
            }

            base = ((Template_List) tempBase);
            nofBaseComps = base.getNofTemplates();
          }

          Template_List templateList = (Template_List) template;
          int nofComponents = templateList.getNofTemplates();
          for (int i = 0; i < nofComponents; i++) {
            ITTCN3Template component = templateList.getTemplateByIndex(i);
            component.setMyGovernor(getOfType());
            if (base != null && nofBaseComps > i) {
              component.setBaseTemplate(base.getTemplateByIndex(i));
            } else {
              component.setBaseTemplate(null);
            }

            component = getOfType().checkThisTemplateRef(timestamp, component);

            switch (component.getTemplatetype()) {
              case PERMUTATION_MATCH:
              case SUPERSET_MATCH:
              case SUBSET_MATCH:
                // FIXME: for Complement??? case COMPLEMENTED_LIST: ???
                // the elements of permutation has to be checked by u.seof.ofType
                // the templates within the permutation always have to be complete
                component.checkThisTemplateGeneric(
                    timestamp, this, false, false, true, true, implicitOmit);
                break;
              case TEMPLATE_NOTUSED:
                if (Completeness_type.MUST_COMPLETE.equals(completeness)) {
                  component.getLocation().reportSemanticError(NOTUSEDNOTALLOWED1);
                } else if (Completeness_type.PARTIAL.equals(completeness) && i >= nofBaseComps) {
                  component.getLocation().reportSemanticError(NOTUSEDNOTALLOWED2);
                }
                break;
              default:
                boolean embeddedModified =
                    (completeness == Completeness_type.MAY_INCOMPLETE)
                        || (completeness == Completeness_type.PARTIAL && i < nofBaseComps);
                component.checkThisTemplateGeneric(
                    timestamp, getOfType(), embeddedModified, false, true, true, implicitOmit);
                break;
            }
          }
          break;
        }
      case INDEXED_TEMPLATE_LIST:
        {
          Map<Long, Integer> indexMap = new HashMap<Long, Integer>();
          Indexed_Template_List indexedTemplateList = (Indexed_Template_List) template;
          for (int i = 0, size = indexedTemplateList.getNofTemplates(); i < size; i++) {
            IndexedTemplate indexedTemplate = indexedTemplateList.getIndexedTemplateByIndex(i);
            Value indexValue = indexedTemplate.getIndex().getValue();
            ITTCN3Template templateComponent = indexedTemplate.getTemplate();

            IReferenceChain chain =
                ReferenceChain.getInstance(IReferenceChain.CIRCULARREFERENCE, true);
            IValue lastValue = indexValue.getValueRefdLast(timestamp, chain);
            chain.release();
            if (Value_type.INTEGER_VALUE.equals(lastValue.getValuetype())) {
              long index = ((Integer_Value) lastValue).getValue();
              if (index > Integer.MAX_VALUE) {
                indexValue
                    .getLocation()
                    .reportSemanticError(
                        MessageFormat.format(
                            TOOBIGINDEXTEMPLATE, Integer.MAX_VALUE, getTypename(), index));
                indexValue.setIsErroneous(true);
              } else if (index < 0) {
                indexValue
                    .getLocation()
                    .reportSemanticError(
                        MessageFormat.format(
                            NONNEGATIVEINDEXEXPECTEDTEMPLATE, getTypename(), index));
                indexValue.setIsErroneous(true);
              } else {
                if (indexMap.containsKey(index)) {
                  indexValue
                      .getLocation()
                      .reportSemanticError(
                          MessageFormat.format(DUPLICATEINDEX, index, i + 1, indexMap.get(index)));
                  indexValue.setIsErroneous(true);
                } else {
                  indexMap.put(index, i);
                }
              }
            } else {
              indexValue.getLocation().reportSemanticError(INTEGERINDEXEXPECTED);
              indexValue.setIsErroneous(true);
            }

            templateComponent.setMyGovernor(getOfType());
            templateComponent = getOfType().checkThisTemplateRef(timestamp, templateComponent);
            templateComponent.checkThisTemplateGeneric(
                timestamp, getOfType(), true, false, true, true, implicitOmit);
          }
          break;
        }
      default:
        template
            .getLocation()
            .reportSemanticError(
                MessageFormat.format(
                    TEMPLATENOTALLOWED, template.getTemplateTypeName(), getTypename()));
        break;
    }
  }
  @Override
  public IType getFieldType(
      final CompilationTimeStamp timestamp,
      final Reference reference,
      final int actualSubReference,
      final Expected_Value_type expectedIndex,
      final IReferenceChain refChain,
      final boolean interruptIfOptional) {
    List<ISubReference> subreferences = reference.getSubreferences();
    if (subreferences.size() <= actualSubReference) {
      return this;
    }

    Expected_Value_type internalExpectation =
        expectedIndex == Expected_Value_type.EXPECTED_TEMPLATE
            ? Expected_Value_type.EXPECTED_DYNAMIC_VALUE
            : expectedIndex;

    ISubReference subreference = subreferences.get(actualSubReference);
    switch (subreference.getReferenceType()) {
      case arraySubReference:
        Value indexValue = ((ArraySubReference) subreference).getValue();
        if (indexValue != null) {
          indexValue.setLoweridToReference(timestamp);
          Type_type tempType = indexValue.getExpressionReturntype(timestamp, expectedIndex);

          switch (tempType) {
            case TYPE_INTEGER:
              IValue last = indexValue.getValueRefdLast(timestamp, expectedIndex, refChain);
              if (Value_type.INTEGER_VALUE.equals(last.getValuetype())) {
                Integer_Value lastInteger = (Integer_Value) last;
                if (lastInteger.isNative()) {
                  long temp = lastInteger.getValue();
                  if (temp < 0) {
                    indexValue
                        .getLocation()
                        .reportSemanticError(MessageFormat.format(NONNEGATIVINDEXEXPECTED, last));
                    indexValue.setIsErroneous(true);
                  }
                } else {
                  indexValue
                      .getLocation()
                      .reportSemanticError(
                          MessageFormat.format(TOOBIGINDEX, indexValue, getTypename()));
                  indexValue.setIsErroneous(true);
                }
              }
              break;
            case TYPE_UNDEFINED:
              indexValue.setIsErroneous(true);
              break;
            default:
              indexValue.getLocation().reportSemanticError(INTEGERINDEXEXPECTED);
              indexValue.setIsErroneous(true);
              break;
          }
        }

        if (getOfType() != null) {
          return getOfType()
              .getFieldType(
                  timestamp,
                  reference,
                  actualSubReference + 1,
                  internalExpectation,
                  refChain,
                  interruptIfOptional);
        }

        return null;
      case fieldSubReference:
        subreference
            .getLocation()
            .reportSemanticError(
                MessageFormat.format(
                    FieldSubReference.INVALIDSUBREFERENCE,
                    ((FieldSubReference) subreference).getId().getDisplayName(),
                    getTypename()));
        return null;
      case parameterisedSubReference:
        subreference
            .getLocation()
            .reportSemanticError(
                MessageFormat.format(
                    FieldSubReference.INVALIDSUBREFERENCE,
                    ((ParameterisedSubReference) subreference).getId().getDisplayName(),
                    getTypename()));
        return null;
      default:
        subreference.getLocation().reportSemanticError(ISubReference.INVALIDSUBREFERENCE);
        return null;
    }
  }
  /**
   * Checks the SequenceOf_value kind value against this type.
   *
   * <p>Please note, that this function can only be called once we know for sure that the value is
   * of set-of type.
   *
   * @param timestamp the timestamp of the actual semantic check cycle.
   * @param value the value to be checked
   * @param expectedValue the kind of value expected here.
   * @param incompleteAllowed wheather incomplete value is allowed or not.
   * @param implicit_omit true if the implicit omit optional attribute was set for the value, false
   *     otherwise
   */
  public void checkThisValueSequenceOf(
      final CompilationTimeStamp timestamp,
      final SequenceOf_Value value,
      final Expected_Value_type expectedValue,
      final boolean incompleteAllowed,
      final boolean implicit_omit,
      final boolean strElem) {
    if (value.isIndexed()) {
      boolean checkHoles = Expected_Value_type.EXPECTED_CONSTANT.equals(expectedValue);
      BigInteger maxIndex = BigInteger.valueOf(-1);
      Map<BigInteger, Integer> indexMap =
          new HashMap<BigInteger, Integer>(value.getNofComponents());
      for (int i = 0, size = value.getNofComponents(); i < size; i++) {
        IValue component = value.getValueByIndex(i);
        IValue index = value.getIndexByIndex(i);
        IReferenceChain referenceChain =
            ReferenceChain.getInstance(IReferenceChain.CIRCULARREFERENCE, true);
        IValue indexLast = index.getValueRefdLast(timestamp, referenceChain);
        referenceChain.release();

        if (indexLast.getIsErroneous(timestamp)
            || !Value_type.INTEGER_VALUE.equals(indexLast.getValuetype())) {
          checkHoles = false;
        } else {
          BigInteger tempIndex = ((Integer_Value) indexLast).getValueValue();
          if (tempIndex.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) == 1) {
            index
                .getLocation()
                .reportSemanticError(
                    MessageFormat.format(
                        "A integer value less than `{0}'' was expected for indexing type `{1}'' instead of `{2}''",
                        Integer.MAX_VALUE, getTypename(), tempIndex));
            checkHoles = false;
          } else if (tempIndex.compareTo(BigInteger.ZERO) == -1) {
            index
                .getLocation()
                .reportSemanticError(
                    MessageFormat.format(
                        "A non-negative integer value was expected for indexing type `{0}'' instead of `{1}''",
                        getTypename(), tempIndex));
            checkHoles = false;
          } else if (indexMap.containsKey(tempIndex)) {
            index
                .getLocation()
                .reportSemanticError(
                    MessageFormat.format(
                        "Duplicate index value `{0}'' for components {1} and {2}",
                        tempIndex, indexMap.get(tempIndex), i + 1));
            checkHoles = false;
          } else {
            indexMap.put(tempIndex, Integer.valueOf(i + 1));
            if (maxIndex.compareTo(tempIndex) == -1) {
              maxIndex = tempIndex;
            }
          }
        }

        component.setMyGovernor(getOfType());
        IValue tempValue2 = getOfType().checkThisValueRef(timestamp, component);
        getOfType()
            .checkThisValue(
                timestamp,
                tempValue2,
                new ValueCheckingOptions(
                    expectedValue, incompleteAllowed, false, true, implicit_omit, strElem));
      }
      if (checkHoles) {
        if (maxIndex.compareTo(BigInteger.valueOf(indexMap.size() - 1)) != 0) {
          value
              .getLocation()
              .reportSemanticError("It's not allowed to create hole(s) in constant values");
        }
      }
    } else {
      for (int i = 0, size = value.getNofComponents(); i < size; i++) {
        IValue component = value.getValueByIndex(i);
        component.setMyGovernor(getOfType());
        if (Value_type.NOTUSED_VALUE.equals(component.getValuetype())) {
          if (!incompleteAllowed) {
            component.getLocation().reportSemanticError(INCOMPLETEPRESENTERROR);
          }
        } else {
          IValue tempValue2 = getOfType().checkThisValueRef(timestamp, component);
          getOfType()
              .checkThisValue(
                  timestamp,
                  tempValue2,
                  new ValueCheckingOptions(
                      expectedValue, incompleteAllowed, false, true, implicit_omit, strElem));
        }
      }
    }
  }