package com.hubspot.jinjava.el.ext;

import java.util.List;
import javax.el.ELContext;
import javax.el.ListELResolver;
import org.apache.commons.lang3.StringUtils;

public class JinjavaListELResolver extends ListELResolver {

  public JinjavaListELResolver(boolean readOnly) {
    super(readOnly);
  }

  @Override
  public Class<?> getType(ELContext context, Object base, Object property) {
    try {
      return super.getType(context, base, property);
    } catch (IllegalArgumentException e) {
      return null;
    }
  }

  @Override
  public boolean isReadOnly(ELContext context, Object base, Object property) {
    try {
      return super.isReadOnly(context, base, property);
    } catch (IllegalArgumentException e) {
      return false;
    }
  }

  @Override
  public Object getValue(ELContext context, Object base, Object property) {
    try {
      // If we're dealing with a negative index, convert it to a positive one.
      if (isResolvable(base)) {
        int index = toIndex(property);
        if (index < 0) {
          // Leave the range checking to the superclass.
          property = index + ((List<?>) base).size();
        }
      }
      return super.getValue(context, base, property);
    } catch (IllegalArgumentException e) {
      return null;
    }
  }

  /**
   * Copied from the unfortunately private ListELResolver.isResolvable
   */
  private static boolean isResolvable(Object base) {
    return base instanceof List<?>;
  }

  /**
   * Convert the given property to an index.  Inspired by
   * ListELResolver.toIndex, but without the base param since we only use it for
   * getValue where base is null.
   *
   * @param property
   *            The name of the property to analyze. Will be coerced to a String.
   * @return The index of property in base.
   * @throws IllegalArgumentException
   *             if property cannot be coerced to an integer.
   */
  private static int toIndex(Object property) {
    int index;
    if (property instanceof Number) {
      index = ((Number) property).intValue();
    } else if (property instanceof String) {
      if (!StringUtils.isNumeric((String) property)) {
        throw new IllegalArgumentException("Cannot parse list index: " + property);
      }
      try {
        // ListELResolver uses valueOf, but findbugs complains.
        index = Integer.parseInt((String) property);
      } catch (NumberFormatException e) {
        throw new IllegalArgumentException("Cannot parse list index: " + property);
      }
    } else if (property instanceof Character) {
      index = ((Character) property).charValue();
    } else if (property instanceof Boolean) {
      index = ((Boolean) property).booleanValue() ? 1 : 0;
    } else {
      throw new IllegalArgumentException(
        "Cannot coerce property to list index: " + property
      );
    }
    return index;
  }

  @Override
  public void setValue(ELContext context, Object base, Object property, Object value) {
    try {
      super.setValue(context, base, property, value);
    } catch (IllegalArgumentException ignored) {}
  }
}

@@ -115,53 +115,8 @@
 ver;
-%0Aimport org.apache.commons.lang3.StringUtils;
 %0A%0Apu
@@ -2057,20 +2057,8 @@
 f (!
-StringUtils.
 isNu
@@ -3013,11 +3013,332 @@
 ) %7B%7D%0A  %7D
+%0A%0A  public static boolean isNumeric(final CharSequence cs) %7B%0A    if (cs == null %7C%7C cs.length() == 0) %7B%0A      return false;%0A    %7D%0A    final int sz = cs.length();%0A    for (int i = 0; i %3C sz; i++) %7B%0A      if (!Character.isDigit(cs.charAt(i)) && cs.charAt(i) != '-') %7B%0A        return false;%0A      %7D%0A    %7D%0A    return true;%0A  %7D
 %0A%7D%0A

use our own isNumeric to account for -