/*
* 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 array
of a non-primitive elements to the corresponding TemplateModel
interface(s), most importantly to TemplateHashModelEx
. If you aren't wrapping an already existing array
, but build a sequence specifically to be used from a template, also consider using SimpleSequence
(see comparison there). Thread safety: A 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).
This adapter is used by DefaultObjectWrapper
if its useAdaptersForCollections
property is true
, which is the default when its incompatibleImprovements
property is 2.3.22 or higher.
See Also: Since: 2.3.22
/**
* 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.
Params: - array – The array to adapt; can't be
null
. Must be an array. - wrapper – The
ObjectWrapper
used to wrap the items in the array. Has to be ObjectWrapperAndUnwrapper
because of planned future features.
/**
* 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.
/**
* 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;
}
}
}