private void validateArrayElement(
     ArrayElement element, FieldTreeNode refMd, RValueExpression rvalue, Path refPath) {
   if (element instanceof ObjectArrayElement) {
     if (refMd != null && !refMd.getType().equals(element.getType())) {
       throw new EvaluationError(
           CrudConstants.ERR_INVALID_ASSIGNMENT + arrayField + " <- " + refPath);
     } else {
       if (rvalue.getType() == RValueExpression.RValueType._value) {
         Value v = rvalue.getValue();
         if (!(v.getValue() instanceof ObjectNode) && !(v.getValue() instanceof ArrayNode))
           throw new EvaluationError(CrudConstants.ERR_EXPECTED_OBJECT_VALUE + arrayField);
       }
     }
   } else {
     if (refMd != null && !refMd.getType().equals(element.getType())) {
       throw new EvaluationError(
           CrudConstants.ERR_INVALID_ASSIGNMENT + arrayField + "<-" + refPath);
     } else {
       if (rvalue.getType() == RValueExpression.RValueType._value) {
         Value v = rvalue.getValue();
         if (v.getValue() instanceof ObjectNode || v.getValue() instanceof ArrayNode)
           throw new EvaluationError(CrudConstants.ERR_EXPECTED_VALUE + arrayField);
       }
     }
   }
 }
 public ArrayAddExpressionEvaluator(
     JsonNodeFactory factory, FieldTreeNode context, ArrayAddExpression expr) {
   this.factory = factory;
   if (expr.getOp() == UpdateOperator._insert) {
     // Path should include an index
     if (expr.getField().isIndex(expr.getField().numSegments() - 1)) {
       arrayField = expr.getField().prefix(-1);
       insertionIndex = expr.getField().getIndex(expr.getField().numSegments() - 1);
     } else {
       throw new EvaluationError(CrudConstants.ERR_REQUIRED_INSERTION_INDEX + expr.getField());
     }
   } else {
     arrayField = expr.getField();
     insertionIndex = -1;
   }
   if (arrayField.nAnys() > 0) {
     throw new EvaluationError(CrudConstants.ERR_PATTERN_NOT_EXPECTED + arrayField);
   }
   FieldTreeNode ftn = context.resolve(arrayField);
   if (ftn instanceof ArrayField) {
     fieldMd = (ArrayField) ftn;
     // Array size field should be at the same level as the array field
     MutablePath abs = new MutablePath();
     fieldMd.getFullPath(abs);
     absArrayField = abs.mutableCopy();
     abs.setLast(abs.getLast() + "#");
     // At this point, arraySizeField is derived from metadata,
     // so it has * as array indexes
     arraySizeField = abs.immutableCopy();
     values = new ArrayList<>(expr.getValues().size());
     initializeArrayField(context, expr);
   } else {
     throw new EvaluationError(CrudConstants.ERR_REQUIRED_ARRAY + arrayField);
   }
 }
  private void initializeArrayField(FieldTreeNode context, ArrayAddExpression expr) {
    for (RValueExpression rvalue : expr.getValues()) {
      Path refPath = null;
      FieldTreeNode refMd = null;
      if (rvalue.getType() == RValueExpression.RValueType._dereference) {
        refPath = rvalue.getPath();
        refMd = context.resolve(refPath);
        if (refMd == null) {
          throw new EvaluationError(CrudConstants.ERR_INVALID_DEREFERENCE + refPath);
        }
      }

      ArrayElement element = fieldMd.getElement();
      validateArrayElement(element, refMd, rvalue, refPath);

      values.add(
          new RValueData(
              refPath,
              refMd == null ? null : refMd.getType(),
              rvalue.getValue(),
              rvalue.getType()));
    }
  }