package com.hubspot.jinjava.lib.filter;

import static com.hubspot.jinjava.util.Logging.ENGINE_LOG;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
import com.hubspot.jinjava.objects.date.PyishDate;

@JinjavaDoc(
    value = "Pretty print a variable. Useful for debugging.",
    input = @JinjavaParam(value = "value", type = "object", desc = "Object to Pretty Print", required = true),
    snippets = {
        @JinjavaSnippet(
            code = "{% set this_var =\"Variable that I want to debug\" %}\n" +
                "{{ this_var|pprint }}")
    })
public class PrettyPrintFilter implements Filter {

  @Override
  public String getName() {
    return "pprint";
  }

  @Override
  public Object filter(Object var, JinjavaInterpreter interpreter, String... args) {
    if (var == null) {
      return "null";
    }

    String varStr = null;

    if (var instanceof String || var instanceof Number || var instanceof PyishDate || var instanceof Iterable || var instanceof Map) {
      varStr = Objects.toString(var);
    }
    else {
      varStr = objPropsToString(var);
    }

    return StringEscapeUtils.escapeHtml4("{% raw %}(" + var.getClass().getSimpleName() + ": " + varStr + "){% endraw %}");
  }

  private String objPropsToString(Object var) {
    List<String> props = new LinkedList<>();

    try {
      BeanInfo beanInfo = Introspector.getBeanInfo(var.getClass());

      for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
        try {
          if (pd.getPropertyType().equals(Class.class)) {
            continue;
          }

          Method readMethod = pd.getReadMethod();
          if (readMethod != null && !readMethod.getDeclaringClass().equals(Object.class)) {
            props.add(pd.getName() + "=" + readMethod.invoke(var));
          }
        } catch (Exception e) {
          ENGINE_LOG.error("Error reading bean value", e);
        }
      }

    } catch (IntrospectionException e) {
      ENGINE_LOG.error("Error inspecting bean", e);
    }

    return '{' + StringUtils.join(props, ", ") + '}';
  }

}

@@ -2049,16 +2049,48 @@
 tyType()
+ != null && pd.getPropertyType()
 .equals(

Fix NPE in PrettyPrintFilter (#286)