/*
* 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;
}
}
}