@Override
  public void checkThisValue(
      final CompilationTimeStamp timestamp,
      final IValue value,
      final ValueCheckingOptions valueCheckingOptions) {
    super.checkThisValue(timestamp, value, valueCheckingOptions);

    IValue last = value.getValueRefdLast(timestamp, valueCheckingOptions.expected_value, null);
    if (null == last || last.getIsErroneous(timestamp)) {
      return;
    }

    // already handled ones
    switch (value.getValuetype()) {
      case OMIT_VALUE:
      case REFERENCED_VALUE:
        return;
      case UNDEFINED_LOWERIDENTIFIER_VALUE:
        if (Value_type.REFERENCED_VALUE.equals(last.getValuetype())) {
          return;
        }
        break;
      default:
        break;
    }

    switch (last.getValuetype()) {
      case UNDEFINED_BLOCK:
        last = last.setValuetype(timestamp, Value_type.CHARSYMBOLS_VALUE);
        if (last.getIsErroneous(timestamp)) {
          return;
        }

        last.setValuetype(timestamp, Value_type.UNIVERSALCHARSTRING_VALUE);
        break;
      case CHARSYMBOLS_VALUE:
      case CHARSTRING_VALUE:
        last.setValuetype(timestamp, Value_type.UNIVERSALCHARSTRING_VALUE);
        break;
      case ISO2022STRING_VALUE:
        location.reportSemanticError(UniversalCharstring_Value.ISOCONVERTION);
        setIsErroneous(true);
        break;
      case UNIVERSALCHARSTRING_VALUE:
        break;
      case EXPRESSION_VALUE:
      case MACRO_VALUE:
        // already checked
        break;
      default:
        value.getLocation().reportSemanticError(BMPSTRINGVALUEEXPECTED);
        value.setIsErroneous(true);
    }
  }
  @Override
  public IValue evaluateValue(
      final CompilationTimeStamp timestamp,
      final Expected_Value_type expectedValue,
      final IReferenceChain referenceChain) {
    if (lastTimeChecked != null && !lastTimeChecked.isLess(timestamp)) {
      return lastValue;
    }

    isErroneous = false;
    lastTimeChecked = timestamp;
    lastValue = this;

    if (value == null) {
      return lastValue;
    }

    checkExpressionOperands(timestamp, expectedValue, referenceChain);

    if (getIsErroneous(timestamp) || isUnfoldable(timestamp, referenceChain)) {
      return lastValue;
    }

    IValue last = value.getValueRefdLast(timestamp, referenceChain);
    if (last.getIsErroneous(timestamp)) {
      return lastValue;
    }

    switch (last.getValuetype()) {
      case CHARSTRING_VALUE:
        {
          String string = ((Charstring_Value) last).getValue();
          string = string.trim();

          double number;
          if ("-INF".equals(string)) {
            number = Float.NEGATIVE_INFINITY;
          } else if ("INF".equals(string)) {
            number = Float.POSITIVE_INFINITY;
          } else {
            try {
              number = Double.parseDouble(string);
            } catch (NumberFormatException e) {
              number = 0;
            }
          }
          lastValue = new Real_Value(number);
          break;
        }
      default:
        return this;
    }

    lastValue.copyGeneralProperties(this);
    return lastValue;
  }
  /**
   * Checks the parameters of the expression and if they are valid in their position in the
   * expression or not.
   *
   * @param timestamp the timestamp of the actual semantic check cycle.
   * @param expectedValue the kind of value expected.
   * @param referenceChain a reference chain to detect cyclic references.
   */
  private void checkExpressionOperands(
      final CompilationTimeStamp timestamp,
      final Expected_Value_type expectedValue,
      final IReferenceChain referenceChain) {
    if (value1 == null || value2 == null) {
      return;
    }

    IValue last1 = null;
    IValue last2 = null;
    Integer_Value i1 = new Integer_Value(0);

    value1.setLoweridToReference(timestamp);
    Type_type tempType1 = value1.getExpressionReturntype(timestamp, expectedValue);

    switch (tempType1) {
      case TYPE_INTEGER:
        last1 = value1.getValueRefdLast(timestamp, expectedValue, referenceChain);
        if (!last1.isUnfoldable(timestamp)
            && Value.Value_type.INTEGER_VALUE.equals(last1.getValuetype())) {
          i1 = (Integer_Value) last1;
          if (i1.signum() < 0) {
            value1.getLocation().reportSemanticError(OPERANDERROR2);
            setIsErroneous(true);
          }
        }
        break;
      case TYPE_UNDEFINED:
        setIsErroneous(true);
        break;
      default:
        if (!isErroneous) {
          location.reportSemanticError(OPERANDERROR1);
          setIsErroneous(true);
        }
        break;
    }

    value2.setLoweridToReference(timestamp);
    Type_type tempType2 = value2.getExpressionReturntype(timestamp, expectedValue);

    switch (tempType2) {
      case TYPE_INTEGER:
        last2 = value2.getValueRefdLast(timestamp, expectedValue, referenceChain);
        if (!last2.isUnfoldable(timestamp)
            && Value.Value_type.INTEGER_VALUE.equals(last2.getValuetype())) {
          if (!((Integer_Value) last2).isNative()) {
            value2.getLocation().reportSemanticError(MessageFormat.format(OPERANDERROR6, last2));
            setIsErroneous(true);
          } else {
            long i2 = ((Integer_Value) last2).getValue();
            if (i2 < 0) {
              value2.getLocation().reportSemanticError(OPERANDERROR4);
              setIsErroneous(true);
            } else if (last1 != null
                && !last1.isUnfoldable(timestamp)
                && Value.Value_type.INTEGER_VALUE.equals(last1.getValuetype())) {
              if ((i1.shiftRight((int) i2 * 8)).signum() > 0) {
                location.reportSemanticError(MessageFormat.format(OPERANDERROR5, i1, i2));
                setIsErroneous(true);
              }
            }
          }
        }
        break;
      case TYPE_UNDEFINED:
        setIsErroneous(true);
        break;
      default:
        if (!isErroneous) {
          location.reportSemanticError(OPERANDERROR3);
          setIsErroneous(true);
        }
        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;
    }
  }
  @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;
    }
  }
  /**
   * 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));
        }
      }
    }
  }
  @Override
  public void checkThisValue(
      final CompilationTimeStamp timestamp,
      final IValue value,
      final ValueCheckingOptions valueCheckingOptions) {
    if (getIsErroneous(timestamp)) {
      return;
    }

    super.checkThisValue(timestamp, value, valueCheckingOptions);

    IValue last = value.getValueRefdLast(timestamp, valueCheckingOptions.expected_value, null);
    if (last == null || last.getIsErroneous(timestamp)) {
      return;
    }

    // already handled ones
    switch (value.getValuetype()) {
      case OMIT_VALUE:
      case REFERENCED_VALUE:
        return;
      case UNDEFINED_LOWERIDENTIFIER_VALUE:
        if (Value_type.REFERENCED_VALUE.equals(last.getValuetype())) {
          return;
        }
        break;
      default:
        break;
    }

    if (Value_type.UNDEFINED_BLOCK.equals(last.getValuetype())) {
      last = last.setValuetype(timestamp, Value_type.SEQUENCEOF_VALUE);
    }
    if (last.getIsErroneous(timestamp)) {
      return;
    }

    switch (last.getValuetype()) {
      case SEQUENCEOF_VALUE:
        {
          checkThisValueSequenceOf(
              timestamp,
              (SequenceOf_Value) last,
              valueCheckingOptions.expected_value,
              valueCheckingOptions.incomplete_allowed,
              valueCheckingOptions.implicit_omit,
              valueCheckingOptions.str_elem);
          break;
        }
      case SETOF_VALUE:
        {
          checkThisValueSetOf(
              timestamp,
              (SetOf_Value) last,
              valueCheckingOptions.expected_value,
              valueCheckingOptions.incomplete_allowed,
              valueCheckingOptions.implicit_omit,
              valueCheckingOptions.str_elem);
          break;
        }
      case EXPRESSION_VALUE:
      case MACRO_VALUE:
        // already checked
        break;
      default:
        if (value.isAsn()) {
          value.getLocation().reportSemanticError(SEQOFVALUEEXPECTED);
        } else {
          value.getLocation().reportSemanticError(RECORDOFVALUEEXPECTED);
        }

        value.setIsErroneous(true);
    }

    if (valueCheckingOptions.sub_check) {
      // there is no parent type to check
      if (subType != null) {
        subType.checkThisValue(timestamp, last);
      }
    }
  }