@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;
  }
  @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 (value1 == null || value2 == null) {
      return lastValue;
    }

    checkExpressionOperands(timestamp, expectedValue, referenceChain);

    if (getIsErroneous(timestamp)) {
      return lastValue;
    }

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

    IValue last1 = value1.getValueRefdLast(timestamp, referenceChain);
    IValue last2 = value2.getValueRefdLast(timestamp, referenceChain);
    if (last1.getIsErroneous(timestamp) || last2.getIsErroneous(timestamp)) {
      setIsErroneous(true);
      return lastValue;
    }

    Integer_Value i1 = (Integer_Value) last1;
    long i2 = ((Integer_Value) last2).getValue();
    lastValue = new Octetstring_Value(Int2HexExpression.int2hex(i1, (int) i2 * 2));

    lastValue.copyGeneralProperties(this);
    return lastValue;
  }
  /**
   * 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);
      }
    }
  }