public MapEventBeanPropertyWriter getWriter(String propertyName) {
    if (writablePropertyDescriptors == null) {
      initializeWriters();
    }
    Pair<EventPropertyDescriptor, MapEventBeanPropertyWriter> pair =
        propertyWriters.get(propertyName);
    if (pair != null) {
      return pair.getSecond();
    }

    Property property = PropertyParser.parse(propertyName, false);
    if (property instanceof MappedProperty) {
      MappedProperty mapProp = (MappedProperty) property;
      return new MapEventBeanPropertyWriterMapProp(
          mapProp.getPropertyNameAtomic(), mapProp.getKey());
    }

    if (property instanceof IndexedProperty) {
      IndexedProperty indexedProp = (IndexedProperty) property;
      return new MapEventBeanPropertyWriterIndexedProp(
          indexedProp.getPropertyNameAtomic(), indexedProp.getIndex());
    }

    return null;
  }
  public EventPropertyDescriptor getWritableProperty(String propertyName) {
    if (writablePropertyDescriptors == null) {
      initializeWriters();
    }
    Pair<EventPropertyDescriptor, ? extends EventPropertyWriter> pair =
        propertyWriters.get(propertyName);
    if (pair != null) {
      return pair.getFirst();
    }

    Property property = PropertyParser.parse(propertyName, false);
    if (property instanceof MappedProperty) {
      EventPropertyWriter writer = getWriter(propertyName);
      if (writer == null) {
        return null;
      }
      MappedProperty mapProp = (MappedProperty) property;
      return new EventPropertyDescriptor(
          mapProp.getPropertyNameAtomic(), Object.class, null, false, true, false, true, false);
    }
    if (property instanceof IndexedProperty) {
      EventPropertyWriter writer = getWriter(propertyName);
      if (writer == null) {
        return null;
      }
      IndexedProperty indexedProp = (IndexedProperty) property;
      return new EventPropertyDescriptor(
          indexedProp.getPropertyNameAtomic(), Object.class, null, true, false, true, false, false);
    }
    return null;
  }
  private static Object coerceProperty(
      String propertyName,
      Class containingType,
      Object value,
      Class type,
      EngineImportService engineImportService,
      boolean forceNumeric)
      throws ExprValidationException {
    if (value instanceof ExprNode && type != ExprNode.class) {
      if (value instanceof ExprIdentNode) {
        ExprIdentNode identNode = (ExprIdentNode) value;
        Property prop;
        try {
          prop = PropertyParser.parse(identNode.getFullUnresolvedName(), false);
        } catch (Exception ex) {
          throw new ExprValidationException(
              "Failed to parse property '" + identNode.getFullUnresolvedName() + "'");
        }
        if (!(prop instanceof MappedProperty)) {
          throw new ExprValidationException(
              "Unrecognized property '" + identNode.getFullUnresolvedName() + "'");
        }
        MappedProperty mappedProperty = (MappedProperty) prop;
        if (mappedProperty.getPropertyNameAtomic().toLowerCase().equals(SYSTEM_PROPETIES_NAME)) {
          return System.getProperty(mappedProperty.getKey());
        }
      } else {
        ExprNode exprNode = (ExprNode) value;
        ExprEvaluator evaluator = exprNode.getExprEvaluator();
        if (evaluator == null) {
          throw new ExprValidationException(
              "Failed to evaluate expression '" + exprNode.toExpressionString() + "'");
        }
        value = evaluator.evaluate(null, true, null);
      }
    }

    if (value == null) {
      return null;
    }
    if (value.getClass() == type) {
      return value;
    }
    if (JavaClassHelper.isAssignmentCompatible(value.getClass(), type)) {
      if (forceNumeric
          && JavaClassHelper.getBoxedType(value.getClass()) != JavaClassHelper.getBoxedType(type)
          && JavaClassHelper.isNumeric(type)
          && JavaClassHelper.isNumeric(value.getClass())) {
        value = JavaClassHelper.coerceBoxed((Number) value, JavaClassHelper.getBoxedType(type));
      }
      return value;
    }
    if (JavaClassHelper.isSubclassOrImplementsInterface(value.getClass(), type)) {
      return value;
    }
    if (type.isArray()) {
      if (!(value instanceof Collection)) {
        throw new ExprValidationException(
            "Property '"
                + propertyName
                + "' of class "
                + JavaClassHelper.getClassNameFullyQualPretty(containingType)
                + " expects an array but receives a value of type "
                + value.getClass().getName());
      }
      Object[] items = ((Collection) value).toArray();
      Object coercedArray = Array.newInstance(type.getComponentType(), items.length);
      for (int i = 0; i < items.length; i++) {
        Object coercedValue =
            coerceProperty(
                propertyName + " (array element)",
                type,
                items[i],
                type.getComponentType(),
                engineImportService,
                false);
        Array.set(coercedArray, i, coercedValue);
      }
      return coercedArray;
    }
    if (!(value instanceof Map)) {
      throw new ExprValidationException(
          "Property '"
              + propertyName
              + "' of class "
              + JavaClassHelper.getClassNameFullyQualPretty(containingType)
              + " expects an "
              + JavaClassHelper.getClassNameFullyQualPretty(type)
              + " but receives a value of type "
              + value.getClass().getName());
    }
    Map<String, Object> props = (Map<String, Object>) value;
    return instantiatePopulateObject(props, type, engineImportService);
  }