private int checkNullConstraints(ARecordType recType, BitSet nulls) {
    boolean isNull = false;
    for (int i = 0; i < recType.getFieldTypes().length; i++) {
      if (nulls.get(i) == false) {
        IAType type = recType.getFieldTypes()[i];
        if (type.getTypeTag() != ATypeTag.NULL && type.getTypeTag() != ATypeTag.UNION) {
          return i;
        }

        if (type.getTypeTag() == ATypeTag.UNION) { // union
          List<IAType> unionList = ((AUnionType) type).getUnionList();
          for (int j = 0; j < unionList.size(); j++) {
            if (unionList.get(j).getTypeTag() == ATypeTag.NULL) {
              isNull = true;
              break;
            }
          }
          if (!isNull) {
            return i;
          }
        }
      }
    }
    return -1;
  }
 private ATypeTag getTargetTypeTag(ATypeTag expectedTypeTag, IAType aObjectType)
     throws IOException {
   if (aObjectType == null) {
     return expectedTypeTag;
   }
   if (aObjectType.getTypeTag() != ATypeTag.UNION) {
     final ATypeTag typeTag = aObjectType.getTypeTag();
     if (ATypeHierarchy.canPromote(expectedTypeTag, typeTag)
         || ATypeHierarchy.canDemote(expectedTypeTag, typeTag)) {
       return typeTag;
     } else {
       return null;
     }
     //            return ATypeHierarchy.canPromote(expectedTypeTag, typeTag) ? typeTag : null;
   } else { // union
     List<IAType> unionList = ((AUnionType) aObjectType).getUnionList();
     for (IAType t : unionList) {
       final ATypeTag typeTag = t.getTypeTag();
       if (ATypeHierarchy.canPromote(expectedTypeTag, typeTag)
           || ATypeHierarchy.canDemote(expectedTypeTag, typeTag)) {
         return typeTag;
       }
     }
   }
   return null;
 }
    @Override
    public IJObject access(
        AListVisitablePointable pointable,
        IObjectPool<IJObject, IAType> objectPool,
        IAType listType,
        JObjectPointableVisitor pointableVisitor)
        throws HyracksDataException {
      List<IVisitablePointable> items = pointable.getItems();
      List<IVisitablePointable> itemTags = pointable.getItemTags();
      JList list = pointable.ordered() ? new JOrderedList(listType) : new JUnorderedList(listType);
      IJObject listItem = null;
      int index = 0;
      try {

        for (IVisitablePointable itemPointable : items) {
          IVisitablePointable itemTagPointable = itemTags.get(index);
          ATypeTag itemTypeTag =
              EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(
                  itemTagPointable.getByteArray()[itemTagPointable.getStartOffset()]);
          typeInfo.reset(listType.getType(), listType.getTypeTag());
          switch (itemTypeTag) {
            case RECORD:
              listItem =
                  pointableVisitor.visit((ARecordVisitablePointable) itemPointable, typeInfo);
              break;
            case UNORDEREDLIST:
            case ORDEREDLIST:
              listItem = pointableVisitor.visit((AListVisitablePointable) itemPointable, typeInfo);
              break;
            case ANY:
              throw new IllegalArgumentException(
                  "Cannot parse list item of type " + listType.getTypeTag());
            default:
              IAType itemType = ((AbstractCollectionType) listType).getItemType();
              typeInfo.reset(itemType, itemType.getTypeTag());
              listItem = pointableVisitor.visit((AFlatValuePointable) itemPointable, typeInfo);
          }
          ATypeTag typeTag =
              EnumDeserializer.ATYPETAGDESERIALIZER.deserialize(
                  itemPointable.getByteArray()[itemPointable.getStartOffset()]);

          list.add(listItem);
        }
      } catch (AsterixException exception) {
        throw new HyracksDataException(exception);
      }
      return list;
    }
 private void parseToNumericTarget(ATypeTag typeTag, IAType objectType, DataOutput out)
     throws AsterixException, IOException {
   final ATypeTag targetTypeTag = getTargetTypeTag(typeTag, objectType);
   if (targetTypeTag == null || !parseValue(admLexer.getLastTokenImage(), targetTypeTag, out)) {
     throw new ParseException(
         mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2 + typeTag);
   }
 }
  @Override
  public IAType computeType(
      ILogicalExpression expression,
      IVariableTypeEnvironment env,
      IMetadataProvider<?, ?> metadataProvider)
      throws AlgebricksException {
    AbstractFunctionCallExpression fce = (AbstractFunctionCallExpression) expression;
    if (fce.getArguments().size() != 1) {
      throw new AlgebricksException("Wrong Argument Number.");
    }
    ILogicalExpression arg0 = fce.getArguments().get(0).getValue();
    IAType t0;
    t0 = (IAType) env.getType(arg0);
    if (t0.getTypeTag() != ATypeTag.NULL
        && t0.getTypeTag() != ATypeTag.BINARY
        && (t0.getTypeTag() == ATypeTag.UNION
            && !((AUnionType) t0).getUnionList().contains(BuiltinType.ABINARY))) {
      throw new NotImplementedException("Expects Binary Type.");
    }

    if (t0.getTypeTag() == ATypeTag.NULL) {
      return BuiltinType.ANULL;
    }

    if (t0.getTypeTag() == ATypeTag.BINARY || t0.getTypeTag().equals(ATypeTag.UNION)) {
      return AUnionType.createNullableType(BuiltinType.AINT64, "binary-length-Result");
    }
    throw new AlgebricksException("Cannot compute type");
  }
  private IAType getComplexType(IAType aObjectType, ATypeTag tag) {
    if (aObjectType == null) {
      return null;
    }

    if (aObjectType.getTypeTag() == tag) {
      return aObjectType;
    }

    if (aObjectType.getTypeTag() == ATypeTag.UNION) {
      List<IAType> unionList = ((AUnionType) aObjectType).getUnionList();
      for (int i = 0; i < unionList.size(); i++) {
        if (unionList.get(i).getTypeTag() == tag) {
          return unionList.get(i);
        }
      }
    }
    return null; // wont get here
  }
  private void parseAndCastNumeric(ATypeTag typeTag, IAType objectType, DataOutput out)
      throws AsterixException, IOException {
    final ATypeTag targetTypeTag = getTargetTypeTag(typeTag, objectType);
    DataOutput dataOutput = out;
    if (targetTypeTag != typeTag) {
      castBuffer.reset();
      dataOutput = castBuffer.getDataOutput();
    }

    if (targetTypeTag == null || !parseValue(admLexer.getLastTokenImage(), typeTag, dataOutput)) {
      throw new ParseException(
          mismatchErrorMessage + objectType.getTypeName() + mismatchErrorMessage2 + typeTag);
    }

    // If two type tags are not the same, either we try to promote or demote source type to the
    // target type
    if (targetTypeTag != typeTag) {
      if (ATypeHierarchy.canPromote(typeTag, targetTypeTag)) {
        // can promote typeTag to targetTypeTag
        ITypeConvertComputer promoteComputer =
            ATypeHierarchy.getTypePromoteComputer(typeTag, targetTypeTag);
        if (promoteComputer == null) {
          throw new AsterixException(
              "Can't cast the " + typeTag + " type to the " + targetTypeTag + " type.");
        }
        // do the promotion; note that the type tag field should be skipped
        promoteComputer.convertType(
            castBuffer.getByteArray(),
            castBuffer.getStartOffset() + 1,
            castBuffer.getLength() - 1,
            out);
      } else if (ATypeHierarchy.canDemote(typeTag, targetTypeTag)) {
        // can demote source type to the target type
        ITypeConvertComputer demoteComputer =
            ATypeHierarchy.getTypeDemoteComputer(typeTag, targetTypeTag);
        if (demoteComputer == null) {
          throw new AsterixException(
              "Can't cast the " + typeTag + " type to the " + targetTypeTag + " type.");
        }
        // do the demotion; note that the type tag field should be skipped
        demoteComputer.convertType(
            castBuffer.getByteArray(),
            castBuffer.getStartOffset() + 1,
            castBuffer.getLength() - 1,
            out);
      }
    }
  }
 private void parseConstructor(ATypeTag typeTag, IAType objectType, DataOutput out)
     throws AsterixException, AdmLexerException, IOException {
   final ATypeTag targetTypeTag = getTargetTypeTag(typeTag, objectType);
   if (targetTypeTag != null) {
     DataOutput dataOutput = out;
     if (targetTypeTag != typeTag) {
       castBuffer.reset();
       dataOutput = castBuffer.getDataOutput();
     }
     int token = admLexer.next();
     if (token == AdmLexer.TOKEN_CONSTRUCTOR_OPEN) {
       token = admLexer.next();
       if (token == AdmLexer.TOKEN_STRING_LITERAL) {
         final String unquoted =
             admLexer.getLastTokenImage().substring(1, admLexer.getLastTokenImage().length() - 1);
         if (!parseValue(unquoted, typeTag, dataOutput)) {
           throw new ParseException(
               "Missing deserializer method for constructor: "
                   + AdmLexer.tokenKindToString(token)
                   + ".");
         }
         token = admLexer.next();
         if (token == AdmLexer.TOKEN_CONSTRUCTOR_CLOSE) {
           if (targetTypeTag != typeTag) {
             ITypeConvertComputer promoteComputer =
                 ATypeHierarchy.getTypePromoteComputer(typeTag, targetTypeTag);
             // the availability if the promote computer should be consistent with the availability
             // of a target type
             assert promoteComputer != null;
             // do the promotion; note that the type tag field should be skipped
             promoteComputer.convertType(
                 castBuffer.getByteArray(),
                 castBuffer.getStartOffset() + 1,
                 castBuffer.getLength() - 1,
                 out);
           }
           return;
         }
       }
     }
   }
   throw new ParseException(
       mismatchErrorMessage + objectType.getTypeName() + ". Got " + typeTag + " instead.");
 }
  @Override
  public boolean rewritePost(Mutable<ILogicalOperator> opRef, IOptimizationContext context)
      throws AlgebricksException {
    AbstractLogicalOperator op = (AbstractLogicalOperator) opRef.getValue();
    // current opperator is join
    if (op.getOperatorTag() != LogicalOperatorTag.INNERJOIN
        && op.getOperatorTag() != LogicalOperatorTag.LEFTOUTERJOIN) {
      return false;
    }

    // Find GET_ITEM function.
    AbstractBinaryJoinOperator joinOp = (AbstractBinaryJoinOperator) op;
    Mutable<ILogicalExpression> expRef = joinOp.getCondition();
    Mutable<ILogicalExpression> getItemExprRef = getSimilarityExpression(expRef);
    if (getItemExprRef == null) {
      return false;
    }
    // Check if the GET_ITEM function is on one of the supported similarity-check functions.
    AbstractFunctionCallExpression getItemFuncExpr =
        (AbstractFunctionCallExpression) getItemExprRef.getValue();
    Mutable<ILogicalExpression> argRef = getItemFuncExpr.getArguments().get(0);
    AbstractFunctionCallExpression simFuncExpr = (AbstractFunctionCallExpression) argRef.getValue();
    if (!simFuncs.contains(simFuncExpr.getFunctionIdentifier())) {
      return false;
    }
    // Skip this rule based on annotations.
    if (simFuncExpr.getAnnotations().containsKey(IndexedNLJoinExpressionAnnotation.INSTANCE)) {
      return false;
    }

    List<Mutable<ILogicalOperator>> inputOps = joinOp.getInputs();
    ILogicalOperator leftInputOp = inputOps.get(0).getValue();
    ILogicalOperator rightInputOp = inputOps.get(1).getValue();

    List<Mutable<ILogicalExpression>> inputExps = simFuncExpr.getArguments();

    ILogicalExpression inputExp0 = inputExps.get(0).getValue();
    ILogicalExpression inputExp1 = inputExps.get(1).getValue();

    // left and right expressions are variables
    if (inputExp0.getExpressionTag() != LogicalExpressionTag.VARIABLE
        || inputExp1.getExpressionTag() != LogicalExpressionTag.VARIABLE) {
      return false;
    }

    LogicalVariable inputVar0 = ((VariableReferenceExpression) inputExp0).getVariableReference();
    LogicalVariable inputVar1 = ((VariableReferenceExpression) inputExp1).getVariableReference();

    LogicalVariable leftInputVar;
    LogicalVariable rightInputVar;

    liveVars.clear();
    VariableUtilities.getLiveVariables(leftInputOp, liveVars);
    if (liveVars.contains(inputVar0)) {
      leftInputVar = inputVar0;
      rightInputVar = inputVar1;
    } else {
      leftInputVar = inputVar1;
      rightInputVar = inputVar0;
    }

    List<LogicalVariable> leftInputPKs = context.findPrimaryKey(leftInputVar);
    List<LogicalVariable> rightInputPKs = context.findPrimaryKey(rightInputVar);
    // Bail if primary keys could not be inferred.
    if (leftInputPKs == null || rightInputPKs == null) {
      return false;
    }
    // primary key has only one variable
    if (leftInputPKs.size() != 1 || rightInputPKs.size() != 1) {
      return false;
    }
    IAType leftType =
        (IAType) context.getOutputTypeEnvironment(leftInputOp).getVarType(leftInputVar);
    IAType rightType =
        (IAType) context.getOutputTypeEnvironment(rightInputOp).getVarType(rightInputVar);
    // left-hand side and right-hand side of "~=" has the same type
    IAType left2 = TypeHelper.getNonOptionalType(leftType);
    IAType right2 = TypeHelper.getNonOptionalType(rightType);
    if (!left2.deepEqual(right2)) {
      return false;
    }
    //
    // -- - FIRE - --
    //
    AqlMetadataProvider metadataProvider = ((AqlMetadataProvider) context.getMetadataProvider());
    FunctionIdentifier funcId = FuzzyUtils.getTokenizer(leftType.getTypeTag());
    String tokenizer;
    if (funcId == null) {
      tokenizer = "";
    } else {
      tokenizer = funcId.getName();
    }

    float simThreshold = FuzzyUtils.getSimThreshold(metadataProvider);
    String simFunction = FuzzyUtils.getSimFunction(metadataProvider);

    // finalize AQL+ query
    String prepareJoin;
    switch (joinOp.getJoinKind()) {
      case INNER:
        {
          prepareJoin = "join" + AQLPLUS;
          break;
        }
      case LEFT_OUTER:
        {
          // TODO To make it work for Left Outer Joins, we should permute
          // the #LEFT and #RIGHT at the top of the AQL+ query. But, when
          // doing this, the
          // fuzzyjoin/user-vis-int-vis-user-lot-aqlplus_1.aql (the one
          // doing 3-way fuzzy joins) gives a different result. But even
          // if we don't change the FuzzyJoinRule, permuting the for
          // clauses in fuzzyjoin/user-vis-int-vis-user-lot-aqlplus_1.aql
          // leads to different results, which suggests there is some
          // other sort of bug.
          return false;
          // prepareJoin = "loj" + AQLPLUS;
          // break;
        }
      default:
        {
          throw new IllegalStateException();
        }
    }
    String aqlPlus =
        String.format(
            Locale.US,
            prepareJoin,
            tokenizer,
            tokenizer,
            simFunction,
            simThreshold,
            tokenizer,
            tokenizer,
            simFunction,
            simThreshold,
            simFunction,
            simThreshold,
            simThreshold);

    LogicalVariable leftPKVar = leftInputPKs.get(0);
    LogicalVariable rightPKVar = rightInputPKs.get(0);

    Counter counter = new Counter(context.getVarCounter());

    AQLPlusParser parser = new AQLPlusParser(new StringReader(aqlPlus));
    parser.initScope();
    parser.setVarCounter(counter);
    List<Clause> clauses;
    try {
      clauses = parser.Clauses();
    } catch (ParseException e) {
      throw new AlgebricksException(e);
    }
    // The translator will compile metadata internally. Run this compilation
    // under the same transaction id as the "outer" compilation.
    AqlPlusExpressionToPlanTranslator translator =
        new AqlPlusExpressionToPlanTranslator(
            metadataProvider.getJobId(), metadataProvider, counter, null, null);
    context.setVarCounter(counter.get());

    LogicalOperatorDeepCopyWithNewVariablesVisitor deepCopyVisitor =
        new LogicalOperatorDeepCopyWithNewVariablesVisitor(context);

    translator.addOperatorToMetaScope(new Identifier("#LEFT"), leftInputOp);
    translator.addVariableToMetaScope(new Identifier("$$LEFT"), leftInputVar);
    translator.addVariableToMetaScope(new Identifier("$$LEFTPK"), leftPKVar);

    translator.addOperatorToMetaScope(new Identifier("#RIGHT"), rightInputOp);
    translator.addVariableToMetaScope(new Identifier("$$RIGHT"), rightInputVar);
    translator.addVariableToMetaScope(new Identifier("$$RIGHTPK"), rightPKVar);

    translator.addOperatorToMetaScope(
        new Identifier("#LEFT_1"), deepCopyVisitor.deepCopy(leftInputOp, null));
    translator.addVariableToMetaScope(
        new Identifier("$$LEFT_1"), deepCopyVisitor.varCopy(leftInputVar));
    translator.addVariableToMetaScope(
        new Identifier("$$LEFTPK_1"), deepCopyVisitor.varCopy(leftPKVar));
    deepCopyVisitor.updatePrimaryKeys(context);
    deepCopyVisitor.reset();

    // translator.addOperatorToMetaScope(new Identifier("#LEFT_2"),
    // deepCopyVisitor.deepCopy(leftInputOp, null));
    // translator.addVariableToMetaScope(new Identifier("$$LEFT_2"),
    // deepCopyVisitor.varCopy(leftInputVar));
    // translator.addVariableToMetaScope(new Identifier("$$LEFTPK_2"),
    // deepCopyVisitor.varCopy(leftPKVar));
    // deepCopyVisitor.updatePrimaryKeys(context);
    // deepCopyVisitor.reset();
    //
    // translator.addOperatorToMetaScope(new Identifier("#LEFT_3"),
    // deepCopyVisitor.deepCopy(leftInputOp, null));
    // translator.addVariableToMetaScope(new Identifier("$$LEFT_3"),
    // deepCopyVisitor.varCopy(leftInputVar));
    // translator.addVariableToMetaScope(new Identifier("$$LEFTPK_3"),
    // deepCopyVisitor.varCopy(leftPKVar));
    // deepCopyVisitor.updatePrimaryKeys(context);
    // deepCopyVisitor.reset();

    translator.addOperatorToMetaScope(
        new Identifier("#RIGHT_1"), deepCopyVisitor.deepCopy(rightInputOp, null));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHT_1"), deepCopyVisitor.varCopy(rightInputVar));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHTPK_1"), deepCopyVisitor.varCopy(rightPKVar));
    deepCopyVisitor.updatePrimaryKeys(context);
    deepCopyVisitor.reset();

    // TODO pick side to run Stage 1, currently always picks RIGHT side
    translator.addOperatorToMetaScope(
        new Identifier("#RIGHT_2"), deepCopyVisitor.deepCopy(rightInputOp, null));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHT_2"), deepCopyVisitor.varCopy(rightInputVar));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHTPK_2"), deepCopyVisitor.varCopy(rightPKVar));
    deepCopyVisitor.updatePrimaryKeys(context);
    deepCopyVisitor.reset();

    translator.addOperatorToMetaScope(
        new Identifier("#RIGHT_3"), deepCopyVisitor.deepCopy(rightInputOp, null));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHT_3"), deepCopyVisitor.varCopy(rightInputVar));
    translator.addVariableToMetaScope(
        new Identifier("$$RIGHTPK_3"), deepCopyVisitor.varCopy(rightPKVar));
    deepCopyVisitor.updatePrimaryKeys(context);
    deepCopyVisitor.reset();

    ILogicalPlan plan;
    try {
      plan = translator.translate(clauses);
    } catch (AsterixException e) {
      throw new AlgebricksException(e);
    }
    context.setVarCounter(counter.get());

    ILogicalOperator outputOp = plan.getRoots().get(0).getValue();

    SelectOperator extraSelect = null;
    if (getItemExprRef != expRef) {
      // more than one join condition
      getItemExprRef.setValue(ConstantExpression.TRUE);
      switch (joinOp.getJoinKind()) {
        case INNER:
          {
            extraSelect = new SelectOperator(expRef, false, null);
            extraSelect.getInputs().add(new MutableObject<ILogicalOperator>(outputOp));
            outputOp = extraSelect;
            break;
          }
        case LEFT_OUTER:
          {
            if (((AbstractLogicalOperator) outputOp).getOperatorTag()
                != LogicalOperatorTag.LEFTOUTERJOIN) {
              throw new IllegalStateException();
            }
            LeftOuterJoinOperator topJoin = (LeftOuterJoinOperator) outputOp;
            topJoin.getCondition().setValue(expRef.getValue());
            break;
          }
        default:
          {
            throw new IllegalStateException();
          }
      }
    }
    opRef.setValue(outputOp);
    OperatorPropertiesUtil.typeOpRec(opRef, context);
    return true;
  }
  private void admFromLexerStream(int token, IAType objectType, DataOutput out, Boolean datasetRec)
      throws AsterixException, IOException, AdmLexerException {

    switch (token) {
      case AdmLexer.TOKEN_NULL_LITERAL:
        {
          if (checkType(ATypeTag.NULL, objectType)) {
            nullSerde.serialize(ANull.NULL, out);
          } else {
            throw new ParseException("This field can not be null");
          }
          break;
        }
      case AdmLexer.TOKEN_TRUE_LITERAL:
        {
          if (checkType(ATypeTag.BOOLEAN, objectType)) {
            booleanSerde.serialize(ABoolean.TRUE, out);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
          }
          break;
        }
      case AdmLexer.TOKEN_BOOLEAN_CONS:
        {
          parseConstructor(ATypeTag.BOOLEAN, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_FALSE_LITERAL:
        {
          if (checkType(ATypeTag.BOOLEAN, objectType)) {
            booleanSerde.serialize(ABoolean.FALSE, out);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
          }
          break;
        }
      case AdmLexer.TOKEN_DOUBLE_LITERAL:
        {
          parseToNumericTarget(ATypeTag.DOUBLE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_DOUBLE_CONS:
        {
          parseConstructor(ATypeTag.DOUBLE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_FLOAT_LITERAL:
        {
          parseToNumericTarget(ATypeTag.FLOAT, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_FLOAT_CONS:
        {
          parseConstructor(ATypeTag.FLOAT, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT8_LITERAL:
        {
          parseAndCastNumeric(ATypeTag.INT8, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT8_CONS:
        {
          parseConstructor(ATypeTag.INT8, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT16_LITERAL:
        {
          parseAndCastNumeric(ATypeTag.INT16, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT16_CONS:
        {
          parseConstructor(ATypeTag.INT16, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT_LITERAL:
        {
          // For an INT value without any suffix, we return it as INT64 type value since it is the
          // default integer type.
          parseAndCastNumeric(ATypeTag.INT64, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT32_LITERAL:
        {
          parseAndCastNumeric(ATypeTag.INT32, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT32_CONS:
        {
          parseConstructor(ATypeTag.INT32, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT64_LITERAL:
        {
          parseAndCastNumeric(ATypeTag.INT64, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INT64_CONS:
        {
          parseConstructor(ATypeTag.INT64, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_STRING_LITERAL:
        {
          if (checkType(ATypeTag.STRING, objectType)) {
            final String tokenImage =
                admLexer
                    .getLastTokenImage()
                    .substring(1, admLexer.getLastTokenImage().length() - 1);
            aString.setValue(admLexer.containsEscapes() ? replaceEscapes(tokenImage) : tokenImage);
            stringSerde.serialize(aString, out);
          } else if (checkType(ATypeTag.UUID, objectType)) {
            // Dealing with UUID type that is represented by a string
            final String tokenImage =
                admLexer
                    .getLastTokenImage()
                    .substring(1, admLexer.getLastTokenImage().length() - 1);
            aUUID.fromStringToAMuatbleUUID(tokenImage);
            uuidSerde.serialize(aUUID, out);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
          }
          break;
        }
      case AdmLexer.TOKEN_STRING_CONS:
        {
          parseConstructor(ATypeTag.STRING, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_HEX_CONS:
      case AdmLexer.TOKEN_BASE64_CONS:
        {
          if (checkType(ATypeTag.BINARY, objectType)) {
            if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_OPEN) {
              if (admLexer.next() == AdmLexer.TOKEN_STRING_LITERAL) {
                parseToBinaryTarget(token, admLexer.getLastTokenImage(), out);
                if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_CLOSE) {
                  break;
                }
              }
            }
          }
          throw new ParseException(mismatchErrorMessage + objectType.getTypeName());
        }
      case AdmLexer.TOKEN_DATE_CONS:
        {
          parseConstructor(ATypeTag.DATE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_TIME_CONS:
        {
          parseConstructor(ATypeTag.TIME, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_DATETIME_CONS:
        {
          parseConstructor(ATypeTag.DATETIME, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_INTERVAL_DATE_CONS:
        {
          if (checkType(ATypeTag.INTERVAL, objectType)) {
            if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_OPEN) {
              if (admLexer.next() == AdmLexer.TOKEN_STRING_LITERAL) {
                AIntervalSerializerDeserializer.parseDate(admLexer.getLastTokenImage(), out);

                if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_CLOSE) {
                  break;
                }
              }
            }
          }
          throw new ParseException("Wrong interval data parsing for date interval.");
        }
      case AdmLexer.TOKEN_INTERVAL_TIME_CONS:
        {
          if (checkType(ATypeTag.INTERVAL, objectType)) {
            if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_OPEN) {
              if (admLexer.next() == AdmLexer.TOKEN_STRING_LITERAL) {
                AIntervalSerializerDeserializer.parseTime(admLexer.getLastTokenImage(), out);

                if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_CLOSE) {
                  break;
                }
              }
            }
          }
          throw new ParseException("Wrong interval data parsing for time interval.");
        }
      case AdmLexer.TOKEN_INTERVAL_DATETIME_CONS:
        {
          if (checkType(ATypeTag.INTERVAL, objectType)) {
            if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_OPEN) {
              if (admLexer.next() == AdmLexer.TOKEN_STRING_LITERAL) {
                AIntervalSerializerDeserializer.parseDatetime(admLexer.getLastTokenImage(), out);

                if (admLexer.next() == AdmLexer.TOKEN_CONSTRUCTOR_CLOSE) {
                  break;
                }
              }
            }
          }
          throw new ParseException("Wrong interval data parsing for datetime interval.");
        }
      case AdmLexer.TOKEN_DURATION_CONS:
        {
          parseConstructor(ATypeTag.DURATION, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_YEAR_MONTH_DURATION_CONS:
        {
          parseConstructor(ATypeTag.YEARMONTHDURATION, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_DAY_TIME_DURATION_CONS:
        {
          parseConstructor(ATypeTag.DAYTIMEDURATION, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_POINT_CONS:
        {
          parseConstructor(ATypeTag.POINT, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_POINT3D_CONS:
        {
          parseConstructor(ATypeTag.POINT3D, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_CIRCLE_CONS:
        {
          parseConstructor(ATypeTag.CIRCLE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_RECTANGLE_CONS:
        {
          parseConstructor(ATypeTag.RECTANGLE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_LINE_CONS:
        {
          parseConstructor(ATypeTag.LINE, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_POLYGON_CONS:
        {
          parseConstructor(ATypeTag.POLYGON, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_START_UNORDERED_LIST:
        {
          if (checkType(ATypeTag.UNORDEREDLIST, objectType)) {
            objectType = getComplexType(objectType, ATypeTag.UNORDEREDLIST);
            parseUnorderedList((AUnorderedListType) objectType, out);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
          }
          break;
        }

      case AdmLexer.TOKEN_START_ORDERED_LIST:
        {
          if (checkType(ATypeTag.ORDEREDLIST, objectType)) {
            objectType = getComplexType(objectType, ATypeTag.ORDEREDLIST);
            parseOrderedList((AOrderedListType) objectType, out);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
          }
          break;
        }
      case AdmLexer.TOKEN_START_RECORD:
        {
          if (checkType(ATypeTag.RECORD, objectType)) {
            objectType = getComplexType(objectType, ATypeTag.RECORD);
            parseRecord((ARecordType) objectType, out, datasetRec);
          } else {
            throw new ParseException(mismatchErrorMessage + objectType.getTypeTag());
          }
          break;
        }
      case AdmLexer.TOKEN_UUID_CONS:
        {
          parseConstructor(ATypeTag.UUID, objectType, out);
          break;
        }
      case AdmLexer.TOKEN_EOF:
        {
          break;
        }
      default:
        {
          throw new ParseException(
              "Unexpected ADM token kind: " + AdmLexer.tokenKindToString(token) + ".");
        }
    }
  }