blob: 117e2a6db3d652d242225436ba99fd5d465aa5c1 [file] [log] [blame]
package org.apache.velocity.runtime.parser.node;
/*
* 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.
*/
import org.apache.velocity.context.InternalContextAdapter;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.TemplateInitException;
import org.apache.velocity.runtime.parser.Parser;
import org.apache.velocity.util.DuckType;
import org.apache.velocity.util.StringUtils;
import java.util.AbstractList;
import java.util.Iterator;
import java.util.ListIterator;
/**
* handles the range 'operator' [ n .. m ]
*
* Please look at the Parser.jjt file which is
* what controls the generation of this class.
*
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
*/
public class ASTIntegerRange extends SimpleNode
{
/**
* @param id
*/
public ASTIntegerRange(int id)
{
super(id);
}
/**
* @param p
* @param id
*/
public ASTIntegerRange(Parser p, int id)
{
super(p, id);
}
/**
* @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
*/
public Object jjtAccept(ParserVisitor visitor, Object data)
{
return visitor.visit(this, data);
}
public static class IntegerRange extends AbstractList<Integer>
{
public class RangeIterator implements ListIterator<Integer>
{
private int value;
public RangeIterator()
{
value = left - delta;
}
public RangeIterator(int startIndex)
{
value = left + (startIndex - 1) * delta;
}
@Override
public Integer next()
{
value += delta;
return value;
}
@Override
public boolean hasPrevious()
{
return value != left - delta;
}
@Override
public Integer previous()
{
value -= delta;
return value;
}
@Override
public int nextIndex()
{
return (value + delta - left) * delta;
}
@Override
public int previousIndex()
{
return (value - delta - left) * delta;
}
@Override
public void remove()
{
throw new UnsupportedOperationException("integer range is read only");
}
@Override
public void set(Integer integer)
{
throw new UnsupportedOperationException("integer range is read only");
}
@Override
public void add(Integer integer)
{
throw new UnsupportedOperationException("integer range is read only");
}
@Override
public boolean hasNext()
{
return value != right;
}
}
private int left;
private int right;
private int delta;
public IntegerRange(int left, int right, int delta)
{
this.left = left;
this.right = right;
this.delta = delta;
}
@Override
public Iterator<Integer> iterator()
{
return new RangeIterator();
}
@Override
public Integer get(int index)
{
int ret = left + delta * index;
if (delta > 0 && ret > right || delta < 0 && ret < right)
{
throw new IndexOutOfBoundsException();
}
return ret;
}
@Override
public int indexOf(Object o)
{
int v = DuckType.asNumber(o).intValue();
v -= left;
v *= delta;
return v >= 0 && v < size() ? v : -1;
}
@Override
public int lastIndexOf(Object o)
{
return indexOf(o);
}
@Override
public ListIterator<Integer> listIterator()
{
return new RangeIterator();
}
@Override
public ListIterator<Integer> listIterator(int index)
{
return new RangeIterator(index);
}
@Override
public int size()
{
return Math.abs(right - left) + 1;
}
}
/**
* does the real work. Creates an Vector of Integers with the
* right value range
*
* @param context app context used if Left or Right of .. is a ref
* @return Object array of Integers
* @throws MethodInvocationException
*/
public Object value( InternalContextAdapter context)
throws MethodInvocationException
{
/*
* get the two range ends
*/
Object left = jjtGetChild(0).value( context );
Object right = jjtGetChild(1).value( context );
/*
* if either is null, lets log and bail
*/
if (left == null || right == null)
{
log.error((left == null ? "Left" : "Right")
+ " side of range operator [n..m] has null value."
+ " Operation not possible. "
+ StringUtils.formatFileString(this));
return null;
}
/*
* if not a Number, try to convert
*/
try
{
left = DuckType.asNumber(left);
}
catch (NumberFormatException nfe) {}
try
{
right = DuckType.asNumber(right);
}
catch (NumberFormatException nfe) {}
/*
* if still not a Number, nothing we can do
*/
if ( !( left instanceof Number ) || !( right instanceof Number ))
{
log.error((!(left instanceof Number) ? "Left" : "Right")
+ " side of range operator is not convertible to a Number. "
+ StringUtils.formatFileString(this));
return null;
}
/*
* get the two integer values of the ends of the range
*/
int l = ((Number) left).intValue() ;
int r = ((Number) right).intValue();
/*
* Determine whether the increment is positive or negative.
*/
int delta = ( l >= r ) ? -1 : 1;
/*
* Return the corresponding integer range
*/
return new IntegerRange(l, r, delta);
}
/**
* @throws TemplateInitException
* @see org.apache.velocity.runtime.parser.node.Node#init(org.apache.velocity.context.InternalContextAdapter, java.lang.Object)
*/
public Object init( InternalContextAdapter context, Object data) throws TemplateInitException
{
Object obj = super.init(context, data);
cleanupParserAndTokens(); // drop reference to Parser and all JavaCC Tokens
return obj;
}
}