throw new NonHashException(target, targetModel, env);
}
private TemplateModel dealWithRangeKey(TemplateModel targetModel, RangeModel range, Environment env)
throws UnexpectedTypeException, InvalidReferenceException, TemplateException {
final TemplateSequenceModel targetSeq;
final String targetStr;
if (targetModel instanceof TemplateSequenceModel) {
targetSeq = (TemplateSequenceModel) targetModel;
targetStr = null;
} else {
targetSeq = null;
try {
targetStr = target.evalAndCoerceToString(env);
} catch(NonStringException e) {
throw new UnexpectedTypeException(
target, target.eval(env),
"sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC,
NUMERICAL_KEY_LHO_EXPECTED_TYPES, env);
}
}
final int size = range.size();
final boolean rightUnbounded = range.isRightUnbounded();
final boolean rightAdaptive = range.isRightAdaptive();
// Right bounded empty ranges are accepted even if the begin index is out of bounds. That's because a such range
// produces an empty sequence, which thus doesn't contain any illegal indexes.
if (!rightUnbounded && size == 0) {
return emptyResult(targetSeq != null);
}
final int firstIdx = range.getBegining();
if (firstIdx < 0) {
throw new _MiscTemplateException(keyExpression, new Object[] {
"Negative range start index (", new Integer(firstIdx),
") isn't allowed for a range used for slicing." });
}
final int targetSize = targetStr != null ? targetStr.length() : targetSeq.size();
final int step = range.getStep();
// Right-adaptive increasing ranges can start 1 after the last element of the target, because they are like
// ranges with exclusive end index of at most targetSize. Thence a such range is just an empty list of indexes,
// and thus it isn't out-of-bounds.
// Right-adaptive decreasing ranges has exclusive end -1, so it can't help on a to high firstIndex.
// Right-bounded ranges at this point aren't empty, so the right index surely can't reach targetSize.
if (rightAdaptive && step == 1 ? firstIdx > targetSize : firstIdx >= targetSize) {
throw new _MiscTemplateException(keyExpression, new Object[] {
"Range start index ", new Integer(firstIdx), " is out of bounds, because the sliced ",
(targetStr != null ? "string" : "sequence"),
" has only ", new Integer(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"),
". ", "(Note that indices are 0-based)." });
}
final int resultSize;
if (!rightUnbounded) {
final int lastIdx = firstIdx + (size - 1) * step;
if (lastIdx < 0) {
if (!rightAdaptive) {
throw new _MiscTemplateException(keyExpression, new Object[] {
"Negative range end index (", new Integer(lastIdx),
") isn't allowed for a range used for slicing." });
} else {
resultSize = firstIdx + 1;
}
} else if (lastIdx >= targetSize) {
if (!rightAdaptive) {
throw new _MiscTemplateException(keyExpression, new Object[] {
"Range end index ", new Integer(lastIdx), " is out of bounds, because the sliced ",
(targetStr != null ? "string" : "sequence"),
" has only ", new Integer(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"),
". (Note that indices are 0-based)." });
} else {
resultSize = Math.abs(targetSize - firstIdx);
}
} else {
resultSize = size;
}
} else {
resultSize = targetSize - firstIdx;
}
if (resultSize == 0) {
return emptyResult(targetSeq != null);
}
if (targetSeq != null) {
ArrayList/*<TemplateModel>*/ list = new ArrayList(resultSize);
int srcIdx = firstIdx;
for (int i = 0; i < resultSize; i++) {
list.add(targetSeq.get(srcIdx));
srcIdx += step;
}
// List items are already wrapped, so the wrapper will be null:
return new SimpleSequence(list, null);
} else {