/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package freemarker.core;

import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template._TemplateAPI;

A class that represents a Range between two integers.
/** * A class that represents a Range between two integers. */
final class Range extends Expression { static final int END_INCLUSIVE = 0; static final int END_EXCLUSIVE = 1; static final int END_UNBOUND = 2; static final int END_SIZE_LIMITED = 3; final Expression lho; final Expression rho; final int endType; Range(Expression lho, Expression rho, int endType) { this.lho = lho; this.rho = rho; this.endType = endType; } int getEndType() { return endType; } @Override TemplateModel _eval(Environment env) throws TemplateException { final int begin = lho.evalToNumber(env).intValue(); if (endType != END_UNBOUND) { final int lhoValue = rho.evalToNumber(env).intValue(); return new BoundedRangeModel( begin, endType != END_SIZE_LIMITED ? lhoValue : begin + lhoValue, endType == END_INCLUSIVE, endType == END_SIZE_LIMITED); } else { return _TemplateAPI.getTemplateLanguageVersionAsInt(this) >= _TemplateAPI.VERSION_INT_2_3_21 ? (RangeModel) new ListableRightUnboundedRangeModel(begin) : (RangeModel) new NonListableRightUnboundedRangeModel(begin); } } // Surely this way we can tell that it won't be a boolean without evaluating the range, but why was this important? @Override boolean evalToBoolean(Environment env) throws TemplateException { throw new NonBooleanException(this, new BoundedRangeModel(0, 0, false, false), env); } @Override public String getCanonicalForm() { String rhs = rho != null ? rho.getCanonicalForm() : ""; return lho.getCanonicalForm() + getNodeTypeSymbol() + rhs; } @Override String getNodeTypeSymbol() { switch (endType) { case END_EXCLUSIVE: return "..<"; case END_INCLUSIVE: return ".."; case END_UNBOUND: return ".."; case END_SIZE_LIMITED: return "..*"; default: throw new BugException(endType); } } @Override boolean isLiteral() { boolean rightIsLiteral = rho == null || rho.isLiteral(); return constantValue != null || (lho.isLiteral() && rightIsLiteral); } @Override protected Expression deepCloneWithIdentifierReplaced_inner( String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { return new Range( lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), rho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), endType); } @Override int getParameterCount() { return 2; } @Override Object getParameterValue(int idx) { switch (idx) { case 0: return lho; case 1: return rho; default: throw new IndexOutOfBoundsException(); } } @Override ParameterRole getParameterRole(int idx) { return ParameterRole.forBinaryOperatorOperand(idx); } }