package com.hubspot.jinjava.el.ext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import javax.el.ELContext; import javax.el.ELException; import javax.el.PropertyNotFoundException; import com.hubspot.jinjava.objects.collections.PyList; import de.odysseus.el.misc.LocalMessages; import de.odysseus.el.tree.Bindings; import de.odysseus.el.tree.impl.ast.AstBracket; import de.odysseus.el.tree.impl.ast.AstNode; public class AstRangeBracket extends AstBracket { protected final AstNode rangeMax; public AstRangeBracket(AstNode base, AstNode rangeStart, AstNode rangeMax, boolean lvalue, boolean strict, boolean ignoreReturnType) { super(base, rangeStart, lvalue, strict, ignoreReturnType); this.rangeMax = rangeMax; } @Override public Object eval(Bindings bindings, ELContext context) { Object base = prefix.eval(bindings, context); if (base == null) { throw new PropertyNotFoundException(LocalMessages.get("error.property.base.null", prefix)); } boolean baseIsString = base.getClass().equals(String.class); if (!Iterable.class.isAssignableFrom(base.getClass()) && !base.getClass().isArray() && !baseIsString) { throw new ELException("Property " + prefix + " is not a sequence."); } // https://github.com/HubSpot/jinjava/issues/52 if (baseIsString) { return evalString((String) base, bindings, context); } Object start = property.eval(bindings, context); if (start == null && strict) { return Collections.emptyList(); } if (!(start instanceof Number)) { throw new ELException("Range start is not a number"); } Object end = rangeMax.eval(bindings, context); if (end == null && strict) { return Collections.emptyList(); } if (!(end instanceof Number)) { throw new ELException("Range end is not a number"); } int startNum = ((Number) start).intValue(); int endNum = ((Number) end).intValue(); Iterable<?> baseItr; if (base.getClass().isArray()) { baseItr = Arrays.asList((Object[]) base); } else { baseItr = (Iterable<?>) base; } PyList result = new PyList(new ArrayList<>()); int index = 0; Iterator<?> baseIterator = baseItr.iterator(); while (baseIterator.hasNext()) { Object next = baseIterator.next(); if (index >= startNum) { if (index >= endNum) { break; } result.add(next); } index++; } return result; } private String evalString(String base, Bindings bindings, ELContext context) { int startNum = intVal(property, 0, base.length(), bindings, context); int endNum = intVal(rangeMax, base.length(), base.length(), bindings, context); endNum = Math.min(endNum, base.length()); if (startNum > endNum) { return ""; } return base.substring(startNum, endNum); } private int intVal(AstNode node, int defVal, int baseLength, Bindings bindings, ELContext context) { if (node == null) { return defVal; } Object val = node.eval(bindings, context); if (val == null) { return defVal; } if (!(val instanceof Number)) { throw new ELException("Range start/end is not a number"); } int result = ((Number) val).intValue(); return result >= 0 ? result : baseLength + result; } @Override public String toString() { return "[:]"; } }