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);
   }
 }
  @Override
  public boolean update(JsonDoc doc, FieldTreeNode contextMd, Path contextPath) {
    boolean ret = false;
    Path absPath = new Path(contextPath, arrayField);
    JsonNode node = doc.get(absPath);
    int insertTo = insertionIndex;
    if (node == null || node instanceof NullNode) {
      doc.modify(absPath, node = factory.arrayNode(), true);
    }
    if (node instanceof ArrayNode) {
      ArrayNode arrayNode = (ArrayNode) node;
      for (RValueData rvalueData : values) {
        LOGGER.debug("add element to {} rvalue:{}", absPath, rvalueData);
        Object newValue = null;
        Type newValueType = null;
        JsonNode newValueNode = null;
        if (rvalueData.refPath != null) {
          JsonNode refNode = doc.get(new Path(contextPath, rvalueData.refPath));
          if (refNode != null) {
            newValueNode = refNode.deepCopy();
            newValue = rvalueData.refType.fromJson(newValueNode);
            newValueType = rvalueData.refType;
          }
        } else if (rvalueData.value != null) {
          newValue = rvalueData.value.getValue();
          newValueNode =
              newValue instanceof JsonNode
                  ? (JsonNode) newValue
                  : fieldMd.getElement().getType().toJson(factory, newValue);
          newValueType = fieldMd.getElement().getType();
        } else if (rvalueData.rvalueType == RValueExpression.RValueType._null) {
          newValueNode = factory.nullNode();
        }
        LOGGER.debug(
            "newValueType:{}, newValue:{}, newValueNode:{} ", newValueType, newValue, newValueNode);

        if (insertTo >= 0) {
          // If we're inserting, make sure we have that many elements
          while (arrayNode.size() < insertTo) {
            arrayNode.addNull();
          }

          if (arrayNode.size() > insertTo) {
            arrayNode.insert(insertTo, newValueNode);
          } else {
            arrayNode.add(newValueNode);
          }
          insertTo++;
        } else {
          arrayNode.add(newValueNode);
        }
        ret = true;
      }
      if (ret) {
        // We have to rewrite the array indexes in arraySizeField using
        // the context path
        MutablePath p = new MutablePath(arraySizeField);
        p.rewriteIndexes(contextPath);
        LOGGER.debug("Setting {} = {}", p, arrayNode.size());
        doc.modify(p, factory.numberNode(arrayNode.size()), false);
      }
    }
    return ret;
  }