blob: 578976cb008fbfe7cf7607b5ea7c3b16550e3106 [file] [log] [blame]
/*
* 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.template;
import java.io.Serializable;
import java.lang.reflect.Array;
import freemarker.ext.util.WrapperTemplateModel;
/**
* Adapts an {@code array} of a non-primitive elements to the corresponding {@link TemplateModel} interface(s), most
* importantly to {@link TemplateHashModelEx}. If you aren't wrapping an already existing {@code array}, but build a
* sequence specifically to be used from a template, also consider using {@link SimpleSequence} (see comparison there).
*
* <p>
* Thread safety: A {@link DefaultListAdapter} is as thread-safe as the array that it wraps is. Normally you only
* have to consider read-only access, as the FreeMarker template language doesn't allow writing these sequences (though
* of course, Java methods called from the template can violate this rule).
*
* <p>
* This adapter is used by {@link DefaultObjectWrapper} if its {@code useAdaptersForCollections} property is
* {@code true}, which is the default when its {@code incompatibleImprovements} property is 2.3.22 or higher.
*
* @see SimpleSequence
* @see DefaultListAdapter
* @see TemplateSequenceModel
*
* @since 2.3.22
*/
public abstract class DefaultArrayAdapter extends WrappingTemplateModel implements TemplateSequenceModel,
AdapterTemplateModel, WrapperTemplateModel, Serializable {
/**
* Factory method for creating new adapter instances.
*
* @param array
* The array to adapt; can't be {@code null}. Must be an array.
* @param wrapper
* The {@link ObjectWrapper} used to wrap the items in the array. Has to be
* {@link ObjectWrapperAndUnwrapper} because of planned future features.
*/
public static DefaultArrayAdapter adapt(Object array, ObjectWrapperAndUnwrapper wrapper) {
final Class componentType = array.getClass().getComponentType();
if (componentType == null) {
throw new IllegalArgumentException("Not an array");
}
if (componentType.isPrimitive()) {
if (componentType == int.class) {
return new IntArrayAdapter((int[]) array, wrapper);
}
if (componentType == double.class) {
return new DoubleArrayAdapter((double[]) array, wrapper);
}
if (componentType == long.class) {
return new LongArrayAdapter((long[]) array, wrapper);
}
if (componentType == boolean.class) {
return new BooleanArrayAdapter((boolean[]) array, wrapper);
}
if (componentType == float.class) {
return new FloatArrayAdapter((float[]) array, wrapper);
}
if (componentType == char.class) {
return new CharArrayAdapter((char[]) array, wrapper);
}
if (componentType == short.class) {
return new ShortArrayAdapter((short[]) array, wrapper);
}
if (componentType == byte.class) {
return new ByteArrayAdapter((byte[]) array, wrapper);
}
return new GenericPrimitiveArrayAdapter(array, wrapper);
} else {
return new ObjectArrayAdapter((Object[]) array, wrapper);
}
}
private DefaultArrayAdapter(ObjectWrapper wrapper) {
super(wrapper);
}
public final Object getAdaptedObject(Class hint) {
return getWrappedObject();
}
private static class ObjectArrayAdapter extends DefaultArrayAdapter {
private final Object[] array;
private ObjectArrayAdapter(Object[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(array[index]) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class ByteArrayAdapter extends DefaultArrayAdapter {
private final byte[] array;
private ByteArrayAdapter(byte[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Byte.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class ShortArrayAdapter extends DefaultArrayAdapter {
private final short[] array;
private ShortArrayAdapter(short[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Short.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class IntArrayAdapter extends DefaultArrayAdapter {
private final int[] array;
private IntArrayAdapter(int[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Integer.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class LongArrayAdapter extends DefaultArrayAdapter {
private final long[] array;
private LongArrayAdapter(long[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Long.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class FloatArrayAdapter extends DefaultArrayAdapter {
private final float[] array;
private FloatArrayAdapter(float[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Float.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class DoubleArrayAdapter extends DefaultArrayAdapter {
private final double[] array;
private DoubleArrayAdapter(double[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Double.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class CharArrayAdapter extends DefaultArrayAdapter {
private final char[] array;
private CharArrayAdapter(char[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Character.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
private static class BooleanArrayAdapter extends DefaultArrayAdapter {
private final boolean[] array;
private BooleanArrayAdapter(boolean[] array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < array.length ? wrap(Boolean.valueOf(array[index])) : null;
}
public int size() throws TemplateModelException {
return array.length;
}
public Object getWrappedObject() {
return array;
}
}
/**
* Much slower than the specialized versions; used only as the last resort.
*/
private static class GenericPrimitiveArrayAdapter extends DefaultArrayAdapter {
private final Object array;
private final int length;
private GenericPrimitiveArrayAdapter(Object array, ObjectWrapper wrapper) {
super(wrapper);
this.array = array;
length = Array.getLength(array);
}
public TemplateModel get(int index) throws TemplateModelException {
return index >= 0 && index < length ? wrap(Array.get(array, index)) : null;
}
public int size() throws TemplateModelException {
return length;
}
public Object getWrappedObject() {
return array;
}
}
}