/*
* Written by Gil Tene and Martin Thompson, and released to the public domain,
* as explained at http://creativecommons.org/publicdomain/zero/1.0/
*/
package org.ObjectLayout;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Iterator;
import static java.lang.reflect.Modifier.isFinal;
import static java.lang.reflect.Modifier.isStatic;
/**
* An array of non-replaceable objects.
* <p>
* A structured array contains array element objects of a fixed (at creation time, per array instance) class,
* and can support elements of any class that provides accessible constructors. The elements in a StructuredArray
* are all allocated and constructed at array creation time, and individual elements cannot be removed or
* replaced after array creation. Array elements can be accessed using an index-based accessor methods in
* the form of {@link AbstractStructuredArray#get}() using either int or long indices. Individual element contents
* can then be accessed and manipulated using any and all operations supported by the member element's class.
* <p>
* While simple creation of default-constructed elements and fixed constructor parameters are available through
* the newInstance factory methods, supporting arbitrary member types requires a wider range of construction
* options. The {@link CtorAndArgsProvider} API provides for array creation with arbitrary, user-supplied
* constructors and arguments, the selection of which can take the element index and construction context
* into account.
* <p>
* StructuredArray is designed with semantics specifically chosen and restricted such that a "flat" memory
* layout of the implemented data structure would be possible on optimizing JVMs. Doing so provides for the
* possibility of matching access speed benefits that exist in data structures with similar semantics that
* are supported in other languages (e.g. an array of structs in C-like languages). While fully functional
* on all JVM implementation (of Java SE 7 and above), the semantics are such that a JVM may transparently
* optimise the implementation to provide a compact contiguous layout that facilitates consistent stride
* based memory access and dead-reckoning (as opposed to de-referenced) access to elements
*
* @param <T> The class of the array elements
*/
abstract class AbstractStructuredArray<T> extends AbstractStructuredArrayBase<T> {
private static final Class[] EMPTY_ARG_TYPES = new Class[0];
private static final Object[] EMPTY_ARGS = new Object[0];
private static final MethodHandles.Lookup noLookup = null;
final Field[] fields;
final boolean hasFinalFields;
private final StructuredArrayModel<? extends AbstractStructuredArray<T>, T> arrayModel;
// Single-dimensional newInstance forms:
/**
* Create an array of <code>length</code> elements, each containing an element object of
* type <code>elementClass</code>. Using the <code>elementClass</code>'s default constructor.
*
* @param elementClass of each element in the array
* @param length of the array to create
* @param <T> The class of the array elements
* @return The newly created array
*/
static <T> AbstractStructuredArray<T> _newInstance(
final Class<T> elementClass,
final long length) {
@SuppressWarnings("unchecked")
AbstractStructuredArray<T> instance = _newInstance(AbstractStructuredArray.class, elementClass, length);
return instance;
}
/**
* Create an array of <code>length</code> elements, each containing an element object of
* type <code>elementClass</code>. Using the <code>elementClass</code>'s default constructor.
*
* @param lookup The lookup object to use when resolving constructors
* @param elementClass of each element in the array
* @param length of the array to create
* @param <T> The class of the array elements
* @return The newly created array
*/
static <T> AbstractStructuredArray<T> _newInstance(
MethodHandles.Lookup lookup,
final Class<T> elementClass,
final long length) {
@SuppressWarnings("unchecked")
AbstractStructuredArray<T> instance = _newInstance(lookup, AbstractStructuredArray.class, elementClass, length);
return instance;
}
/**
* Create an <code>arrayClass</code> array of <code>length</code> elements of
* type <code>elementClass</code>. Using the <code>elementClass</code>'s default constructor.
*
* @param arrayClass of the array to create
* @param elementClass of each element in the array
* @param length of the array to create
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final Class<S> arrayClass,
final Class<T> elementClass,
final long length) {
return _newInstance(noLookup, arrayClass, elementClass, length);
}
/**
* Create an <code>arrayClass</code> array of <code>length</code> elements of
* type <code>elementClass</code>. Using the <code>elementClass</code>'s default constructor.
*
* @param lookup The lookup object to use when resolving constructors
* @param arrayClass of the array to create
* @param elementClass of each element in the array
* @param length of the array to create
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
MethodHandles.Lookup lookup,
final Class<S> arrayClass,
final Class<T> elementClass,
final long length) {
CtorAndArgs<S> arrayCtorAndArgs = new CtorAndArgs<>(lookup, arrayClass, EMPTY_ARG_TYPES, EMPTY_ARGS);
return _newInstance(lookup, arrayCtorAndArgs, elementClass, length);
}
/**
* Create an <code>arrayCtorAndArgs.getConstructor().getDeclaringClass()</code> array of <code>length</code>
* elements of type <code>elementClass</code>. Use <code>elementClass</code>'s default constructor.
*
* @param arrayCtorAndArgs for creating the array
* @param elementClass of each element in the array
* @param length of the array to create
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final CtorAndArgs<S> arrayCtorAndArgs,
final Class<T> elementClass,
final long length) {
return _newInstance(noLookup, arrayCtorAndArgs, elementClass, length);
}
/**
* Create an <code>arrayCtorAndArgs.getConstructor().getDeclaringClass()</code> array of <code>length</code>
* elements of type <code>elementClass</code>. Use <code>elementClass</code>'s default constructor.
*
* @param lookup The lookup object to use when resolving constructors
* @param arrayCtorAndArgs for creating the array
* @param elementClass of each element in the array
* @param length of the array to create
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
MethodHandles.Lookup lookup,
final CtorAndArgs<S> arrayCtorAndArgs,
final Class<T> elementClass,
final long length) {
AbstractStructuredArrayBuilder<S, T> arrayBuilder = new AbstractStructuredArrayBuilder<>(
lookup,
arrayCtorAndArgs.getConstructor().getDeclaringClass(),
elementClass,
length).
arrayCtorAndArgs(arrayCtorAndArgs).
resolve();
return instantiate(arrayBuilder);
}
/**
* Create an <code>arrayCtorAndArgs.getConstructor().getDeclaringClass()</code> array of <code>length</code>
* elements of type <code>elementClass</code>. Use <code>elementClass</code>'s default constructor.
*
* @param arrayCtorAndArgs for creating the array
* @param elementCtorAndArgs of each element in the array
* @param length of the array to create
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final CtorAndArgs<S> arrayCtorAndArgs,
final CtorAndArgs<T> elementCtorAndArgs,
final long length) {
AbstractStructuredArrayBuilder<S, T> arrayBuilder = new AbstractStructuredArrayBuilder<>(
arrayCtorAndArgs.getConstructor().getDeclaringClass(),
elementCtorAndArgs.getConstructor().getDeclaringClass(),
length).
arrayCtorAndArgs(arrayCtorAndArgs).
elementCtorAndArgs(elementCtorAndArgs).
resolve();
return instantiate(arrayBuilder);
}
/**
* Create an array of <code>length</code> elements of type <code>elementClass</code>. Use constructor and
* arguments supplied (on a potentially per element index basis) by the specified
* <code>ctorAndArgsProvider</code> to construct and initialize each element.
*
* @param elementClass of each element in the array
* @param ctorAndArgsProvider produces element constructors [potentially] on a per element basis
* @param length of the array to create
* @param <T> The class of the array elements
* @return The newly created array
*/
static <T> AbstractStructuredArray<T> _newInstance(
final Class<T> elementClass,
final CtorAndArgsProvider<T> ctorAndArgsProvider,
final long length) {
return instantiate(elementClass, length, ctorAndArgsProvider);
}
/**
* Create an <code>arrayClass</code> array of <code>length</code> elements, each containing an element object of
* type <code>elementClass</code>. Use constructor and arguments supplied (on a potentially
* per element index basis) by the specified <code>ctorAndArgsProvider</code> to construct and initialize
* each element.
*
* @param arrayClass of the array to create.
* @param elementClass of each element in the array
* @param length of the array to create
* @param ctorAndArgsProvider produces element constructors [potentially] on a per element basis
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final Class<S> arrayClass,
final Class<T> elementClass,
final long length,
final CtorAndArgsProvider<T> ctorAndArgsProvider) {
return _newInstance(noLookup, arrayClass, elementClass, length, ctorAndArgsProvider);
}
/**
* Create an <code>arrayClass</code> array of <code>length</code> elements, each containing an element object of
* type <code>elementClass</code>. Use constructor and arguments supplied (on a potentially
* per element index basis) by the specified <code>ctorAndArgsProvider</code> to construct and initialize
* each element.
*
* @param lookup The lookup object to use when resolving constructors
* @param arrayClass of the array to create.
* @param elementClass of each element in the array
* @param length of the array to create
* @param ctorAndArgsProvider produces element constructors [potentially] on a per element basis
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
MethodHandles.Lookup lookup,
final Class<S> arrayClass,
final Class<T> elementClass,
final long length,
final CtorAndArgsProvider<T> ctorAndArgsProvider) {
CtorAndArgs<S> arrayCtorAndArgs = new CtorAndArgs<>(lookup, arrayClass, EMPTY_ARG_TYPES, EMPTY_ARGS);
return instantiate(arrayCtorAndArgs, elementClass, length, ctorAndArgsProvider);
}
/**
* Create an <code>arrayCtorAndArgs.getConstructor().getDeclaringClass()</code> array of <code>length</code>
* elements of type <code>elementClass</code>. Use constructor and arguments
* supplied (on a potentially per element index basis) by the specified <code>ctorAndArgsProvider</code>
* to construct and initialize each element.
*
* @param arrayCtorAndArgs of the array to create
* @param elementClass of each element in the array
* @param length of the array to create
* @param ctorAndArgsProvider produces element constructors [potentially] on a per element basis
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final CtorAndArgs<S> arrayCtorAndArgs,
final Class<T> elementClass,
final long length,
final CtorAndArgsProvider<T> ctorAndArgsProvider) {
return instantiate(arrayCtorAndArgs, elementClass, length, ctorAndArgsProvider);
}
/**
* Create a <S extends StructuredArray<T>> array instance with elements copied from a source
* collection.
* @param arrayClass of the array to create.
* @param elementClass of each element in the array
* @param sourceCollection provides details for building the array
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final Class<S> arrayClass,
final Class<T> elementClass,
final Collection<T> sourceCollection) {
return _newInstance(noLookup, arrayClass, elementClass, sourceCollection);
}
/**
* Create a <S extends StructuredArray<T>> array instance with elements copied from a source
* collection.
* @param lookup The lookup object to use when resolving constructors
* @param arrayClass of the array to create.
* @param elementClass of each element in the array
* @param sourceCollection provides details for building the array
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
MethodHandles.Lookup lookup,
final Class<S> arrayClass,
final Class<T> elementClass,
final Collection<T> sourceCollection) {
long length = sourceCollection.size();
for (T element : sourceCollection) {
if (element.getClass() != elementClass) {
throw new IllegalArgumentException(
"Collection contains elements of type other than elementClass " + elementClass.getName());
}
}
final CtorAndArgs<T> copyCtorAndArgs;
final Object[] args = new Object[1];
try {
copyCtorAndArgs = new CtorAndArgs<>(lookup, elementClass, new Class[] {elementClass}, args);
} catch (RuntimeException ex) {
throw new IllegalArgumentException("Failed to locate copy constructor and args for elementClass " +
elementClass.getName() + ".", ex);
}
final Iterator<T> sourceIterator = sourceCollection.iterator();
AbstractStructuredArrayBuilder<S, T> arrayBuilder = new AbstractStructuredArrayBuilder<>(
lookup,
arrayClass,
elementClass,
length).
elementCtorAndArgsProvider(
new CtorAndArgsProvider<T>() {
@Override
public CtorAndArgs<T> getForContext(ConstructionContext<T> context) {
args[0] = sourceIterator.next();
return copyCtorAndArgs.setArgs(args);
}
}
);
return instantiate(arrayBuilder);
}
/**
* Create a <S extends StructuredArray<T>> array instance according to the details provided in the
* arrayBuilder.
* @param arrayBuilder provides details for building the array
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _newInstance(
final AbstractStructuredArrayBuilder<S, T> arrayBuilder) {
return instantiate(arrayBuilder);
}
/**
* Copy a given array of elements to a newly created array. Copying of individual elements is done by using
* the <code>elementClass</code> copy constructor to construct the individual member elements of the new
* array based on the corresponding elements of the <code>source</code> array.
*
* @param source The array to duplicate
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
final S source) {
return _copyInstance(noLookup, source);
}
/**
* Copy a given array of elements to a newly created array. Copying of individual elements is done by using
* the <code>elementClass</code> copy constructor to construct the individual member elements of the new
* array based on the corresponding elements of the <code>source</code> array.
*
* @param lookup The lookup object to use when resolving constructors
* @param source The array to duplicate
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
MethodHandles.Lookup lookup,
final S source) {
return _copyInstance(lookup, source, 0, source.getLength());
}
/**
* Copy a range from an array of elements to a newly created array. Copying of individual elements is done
* by using the <code>elementClass</code> copy constructor to construct the individual member elements of
* the new array based on the corresponding elements of the <code>source</code> array.
*
* @param source The array to copy from
* @param sourceOffset offset index, indicating where the source region to be copied begins
* @param count the number of elements to copy
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
final S source,
final long sourceOffset,
final long count) {
return _copyInstance(noLookup, source, sourceOffset, count);
}
/**
* Copy a range from an array of elements to a newly created array. Copying of individual elements is done
* by using the <code>elementClass</code> copy constructor to construct the individual member elements of
* the new array based on the corresponding elements of the <code>source</code> array.
*
* @param lookup The lookup object to use when resolving constructors
* @param source The array to copy from
* @param sourceOffset offset index, indicating where the source region to be copied begins
* @param count the number of elements to copy
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
MethodHandles.Lookup lookup,
final S source,
final long sourceOffset,
final long count) {
return _copyInstance(lookup, source, new long[]{sourceOffset}, new long[]{count});
}
/**
* Copy a range from an array of elements to a newly created array. Copying of individual elements is done
* by using the <code>elementClass</code> copy constructor to construct the individual member elements of
* the new array based on the corresponding elements of the <code>source</code> array.
* <p>
* This form is useful [only] for copying partial ranges from nested StructuredArrays.
* </p>
* @param source The array to copy from
* @param sourceOffsets offset indexes, indicating where the source region to be copied begins at each
* StructuredArray nesting depth
* @param counts the number of elements to copy at each StructuredArray nesting depth
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
@SuppressWarnings("unchecked")
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
final S source,
final long[] sourceOffsets,
final long[] counts) {
return _copyInstance(noLookup, source, sourceOffsets, counts);
}
/**
* Copy a range from an array of elements to a newly created array. Copying of individual elements is done
* by using the <code>elementClass</code> copy constructor to construct the individual member elements of
* the new array based on the corresponding elements of the <code>source</code> array.
* <p>
* This form is useful [only] for copying partial ranges from nested StructuredArrays.
* </p>
* @param lookup The lookup object to use when resolving constructors
* @param source The array to copy from
* @param sourceOffsets offset indexes, indicating where the source region to be copied begins at each
* StructuredArray nesting depth
* @param counts the number of elements to copy at each StructuredArray nesting depth
* @param <S> The class of the array to be created
* @param <T> The class of the array elements
* @return The newly created array
*/
@SuppressWarnings("unchecked")
static <S extends AbstractStructuredArray<T>, T> S _copyInstance(
MethodHandles.Lookup lookup,
final S source,
final long[] sourceOffsets,
final long[] counts) {
if (sourceOffsets.length != counts.length) {
throw new IllegalArgumentException("sourceOffsets.length must match counts.length");
}
// Verify source ranges fit in model:
int depth = 0;
StructuredArrayModel arrayModel = source.getArrayModel();
while((depth < counts.length) && (arrayModel != null) && StructuredArrayModel.class.isInstance(arrayModel)) {
if (arrayModel.getLength() < sourceOffsets[depth] + counts[depth]) {
throw new ArrayIndexOutOfBoundsException(
"At nesting depth " + depth + ", source length (" + arrayModel.getLength() +
") is smaller than sourceOffset (" + sourceOffsets[depth] +
") + count (" + counts[depth] + ")" );
}
arrayModel = (StructuredArrayModel) arrayModel.getStructuredSubArrayModel();
depth++;
}
// If we run out of model depth before we run out of sourceOffsets and counts, throw:
if (depth < counts.length) {
throw new IllegalArgumentException("sourceOffsets.length and counts.length (" + counts.length +
") must not exceed StructuredArray nesting depth (" + depth + ")");
}
final StructuredArrayModel<S, T> sourceArrayModel = (StructuredArrayModel<S, T>) source.getArrayModel();
final Class<S> sourceArrayClass = sourceArrayModel.getArrayClass();
CtorAndArgs<S> arrayCtorAndArgs =
new CtorAndArgs<>(lookup, sourceArrayClass, new Class[] {sourceArrayClass}, source);
final AbstractStructuredArrayBuilder<S, T> arrayBuilder =
createCopyingArrayBuilder(lookup, sourceArrayModel, sourceOffsets, 0, counts, 0).
arrayCtorAndArgs(arrayCtorAndArgs).
contextCookie(source);
return instantiate(arrayBuilder);
}
private static <S extends AbstractStructuredArray<T>, T> AbstractStructuredArrayBuilder<S, T> createCopyingArrayBuilder(
MethodHandles.Lookup lookup,
final StructuredArrayModel<S, T> sourceArrayModel,
final long[] sourceOffsets, final int offsetsIndex,
final long[] counts, final int countsIndex) {
final Class<S> sourceArrayClass = sourceArrayModel.getArrayClass();
final Class<T> elementClass = sourceArrayModel.getElementClass();
long sourceOffset = (offsetsIndex < sourceOffsets.length) ? sourceOffsets[offsetsIndex] : 0;
long count = (countsIndex < counts.length) ? counts[countsIndex] : sourceArrayModel.getLength();
final CtorAndArgsProvider<T> elementCopyCtorAndArgsProvider =
new CopyCtorAndArgsProvider<>(lookup, elementClass, sourceOffset);
if (sourceArrayModel.getStructuredSubArrayModel() != null) {
// This array contains another array:
AbstractStructuredArrayBuilder subArrayBuilder =
createCopyingArrayBuilder(lookup,
(StructuredArrayModel)sourceArrayModel.getStructuredSubArrayModel(),
sourceOffsets, offsetsIndex + 1,
counts, countsIndex + 1);
@SuppressWarnings("unchecked")
AbstractStructuredArrayBuilder<S, T> builder =
new AbstractStructuredArrayBuilder<>(lookup, sourceArrayClass, subArrayBuilder, count).
elementCtorAndArgsProvider(elementCopyCtorAndArgsProvider).
resolve();
return builder;
} else if (sourceArrayModel.getPrimitiveSubArrayModel() != null) {
// This array contains elements that are PrimitiveArrays:
PrimitiveArrayModel model = (PrimitiveArrayModel) sourceArrayModel.getPrimitiveSubArrayModel();
@SuppressWarnings("unchecked")
PrimitiveArrayBuilder subArrayBuilder =
new PrimitiveArrayBuilder(model.getArrayClass(), model.getLength());
@SuppressWarnings("unchecked")
AbstractStructuredArrayBuilder<S, T> builder =
new AbstractStructuredArrayBuilder<>(lookup, sourceArrayClass, subArrayBuilder, count).
elementCtorAndArgsProvider(elementCopyCtorAndArgsProvider).
resolve();
return builder;
} else {
// This is a leaf array (it's elements are regular objects):
return new AbstractStructuredArrayBuilder<>(lookup, sourceArrayClass, elementClass, count).
elementCtorAndArgsProvider(elementCopyCtorAndArgsProvider).
resolve();
}
}
private static <T> AbstractStructuredArray<T> instantiate(
final Class<T> elementClass,
final long length,
final CtorAndArgsProvider<T> ctorAndArgsProvider) {
@SuppressWarnings("unchecked")
AbstractStructuredArrayBuilder<AbstractStructuredArray<T>, T> arrayBuilder =
new AbstractStructuredArrayBuilder(AbstractStructuredArray.class, elementClass, length).
elementCtorAndArgsProvider(ctorAndArgsProvider).resolve();
return instantiate(arrayBuilder);
}
private static <S extends AbstractStructuredArray<T>, T> S instantiate(
final CtorAndArgs<S> arrayCtorAndArgs,
final Class<T> elementClass,
final long length,
final CtorAndArgsProvider<T> ctorAndArgsProvider) {
@SuppressWarnings("unchecked")
AbstractStructuredArrayBuilder<S, T> arrayBuilder =
new AbstractStructuredArrayBuilder(arrayCtorAndArgs.getConstructor().getDeclaringClass(), elementClass, length).
arrayCtorAndArgs(arrayCtorAndArgs).
elementCtorAndArgsProvider(ctorAndArgsProvider).
resolve();
return instantiate(arrayBuilder);
}
private static <S extends AbstractStructuredArray<T>, T> S instantiate(
final AbstractStructuredArrayBuilder<S, T> arrayBuilder) {
ConstructionContext<T> context = new ConstructionContext<>(arrayBuilder.getContextCookie());
ConstructorMagic constructorMagic = getConstructorMagic();
constructorMagic.setConstructionArgs(arrayBuilder, context);
try {
arrayBuilder.resolve();
constructorMagic.setActive(true);
StructuredArrayModel<S, T> arrayModel = arrayBuilder.getArrayModel();
Constructor<S> constructor = arrayBuilder.getArrayCtorAndArgs().getConstructor();
Object[] args = arrayBuilder.getArrayCtorAndArgs().getArgs();
return AbstractStructuredArrayBase.instantiateStructuredArray(arrayModel, constructor, args);
} finally {
constructorMagic.setActive(false);
}
}
protected AbstractStructuredArray() {
checkConstructorMagic();
// Extract locally needed args from constructor magic:
ConstructorMagic constructorMagic = getConstructorMagic();
@SuppressWarnings("unchecked")
final ConstructionContext<T> context = constructorMagic.getContext();
@SuppressWarnings("unchecked")
final AbstractStructuredArrayBuilder<AbstractStructuredArray<T>, T> arrayBuilder = constructorMagic.getArrayBuilder();
final CtorAndArgsProvider<T> ctorAndArgsProvider = arrayBuilder.getElementCtorAndArgsProvider();
// Finish consuming constructMagic arguments:
constructorMagic.setActive(false);
context.setArray(this);
this.arrayModel = arrayBuilder.getArrayModel();
final Field[] fields = removeStaticFields(getElementClass().getDeclaredFields());
for (final Field field : fields) {
field.setAccessible(true);
}
this.fields = fields;
this.hasFinalFields = containsFinalQualifiedFields(fields);
AbstractStructuredArrayBuilder structuredSubArrayBuilder = arrayBuilder.getStructuredSubArrayBuilder();
PrimitiveArrayBuilder primitiveSubArrayBuilder = arrayBuilder.getPrimitiveSubArrayBuilder();
if (structuredSubArrayBuilder != null) {
populateStructuredSubArrays(ctorAndArgsProvider, structuredSubArrayBuilder, context);
} else if (primitiveSubArrayBuilder != null) {
populatePrimitiveSubArrays(ctorAndArgsProvider, primitiveSubArrayBuilder, context);
} else {
// This is a single dimension array. Populate it:
populateLeafElements(ctorAndArgsProvider, context);
}
}
protected AbstractStructuredArray(AbstractStructuredArray<T> sourceArray) {
// Support copy constructor. When we get here, everything is already set up for the regular
// (default) construction path to perform the required copy.
// Copying will actually be done according to the CtorAndArgsProvider and context already supplied,
// with top-most source array being (already) passed as the contextCookie in the builder's
// construction context, and copying proceeding using the context indices and the supplied
// contextCookie provided by each CtorAndArgsProvider to figure out individual sources.
this();
}
/**
* Get the length (number of elements) of the array.
*
* @return the number of elements in the array.
*/
long getLength() {
return super.getLength();
}
/**
* Get the {@link Class} of elements stored in the array.
*
* @return the {@link Class} of elements stored in the array.
*/
Class<T> getElementClass() {
return super.getElementClass();
}
/**
* Get the array model
* @return a model of this array
*/
StructuredArrayModel<? extends AbstractStructuredArray<T>, T> getArrayModel() {
return arrayModel;
}
/**
* Get a reference to an element in a single dimensional array, using a <code>long</code> index.
*
* @param index of the element to retrieve.
* @return a reference to the indexed element.
*/
T get(final long index) throws IllegalArgumentException {
return super.get(index);
}
/**
* Get a reference to an element in a single dimensional array, using an <code>int</code> index.
*
* @param index of the element to retrieve.
* @return a reference to the indexed element.
*/
T get(final int index) throws IllegalArgumentException {
return super.get(index);
}
//
//
// Populating array elements:
//
//
private void populateLeafElement(final long index,
CtorAndArgs<T> ctorAndArgs) {
// Instantiate:
constructElementAtIndex(index, ctorAndArgs.getConstructor(), ctorAndArgs.getArgs());
}
private void populatePrimitiveSubArray(final long index,
PrimitiveArrayBuilder subArrayBuilder,
final CtorAndArgs<T> subArrayCtorAndArgs) {
// Instantiate:
constructPrimitiveSubArrayAtIndex(
index,
subArrayBuilder.getArrayModel(),
subArrayCtorAndArgs.getConstructor(),
subArrayCtorAndArgs.getArgs());
}
private void populateStructuredSubArray(final ConstructionContext<T> context,
AbstractStructuredArrayBuilder subArrayBuilder,
final CtorAndArgs<T> subArrayCtorAndArgs) {
ConstructionContext<T> subArrayContext = new ConstructionContext<>(subArrayCtorAndArgs.getContextCookie());
subArrayContext.setContainingContext(context);
ConstructorMagic constructorMagic = getConstructorMagic();
constructorMagic.setConstructionArgs(subArrayBuilder, subArrayContext);
try {
constructorMagic.setActive(true);
// Instantiate:
constructSubArrayAtIndex(
context.getIndex(),
subArrayBuilder.getArrayModel(),
subArrayCtorAndArgs.getConstructor(),
subArrayCtorAndArgs.getArgs());
} finally {
constructorMagic.setActive(false);
}
}
private void populateLeafElements(final CtorAndArgsProvider<T> ctorAndArgsProvider,
final ConstructionContext<T> context) {
long length = getLength();
try {
for (long index = 0; index < length; index++) {
final CtorAndArgs<T> ctorAndArgs;
context.setIndex(index);
ctorAndArgs = ctorAndArgsProvider.getForContext(context);
if (ctorAndArgs.getConstructor().getDeclaringClass() != getElementClass()) {
throw new IllegalArgumentException("ElementClass (" + getElementClass() +
") does not match ctorAndArgs.getConstructor().getDeclaringClass() (" +
ctorAndArgs.getConstructor().getDeclaringClass() + ")");
}
populateLeafElement(index, ctorAndArgs);
}
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
private void populatePrimitiveSubArrays(final CtorAndArgsProvider<T> subArrayCtorAndArgsProvider,
final PrimitiveArrayBuilder subArrayBuilder,
final ConstructionContext<T> context) {
long length = getLength();
try {
for (long index = 0; index < length; index++) {
final CtorAndArgs<T> ctorAndArgs;
context.setIndex(index);
ctorAndArgs = subArrayCtorAndArgsProvider.getForContext(context);
if (ctorAndArgs.getConstructor().getDeclaringClass() != getElementClass()) {
throw new IllegalArgumentException("ElementClass (" + getElementClass() +
") does not match ctorAndArgs.getConstructor().getDeclaringClass() (" +
ctorAndArgs.getConstructor().getDeclaringClass() + ")");
}
populatePrimitiveSubArray(index, subArrayBuilder, ctorAndArgs);
}
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
private void populateStructuredSubArrays(final CtorAndArgsProvider<T> subArrayCtorAndArgsProvider,
final AbstractStructuredArrayBuilder subArrayBuilder,
final ConstructionContext<T> context) {
long length = getLength();
try {
for (long index = 0; index < length; index++) {
final CtorAndArgs<T> ctorAndArgs;
context.setIndex(index);
ctorAndArgs = subArrayCtorAndArgsProvider.getForContext(context);
if (ctorAndArgs.getConstructor().getDeclaringClass() != getElementClass()) {
throw new IllegalArgumentException("ElementClass (" + getElementClass() +
") does not match ctorAndArgs.getConstructor().getDeclaringClass() (" +
ctorAndArgs.getConstructor().getDeclaringClass() + ")");
}
populateStructuredSubArray(context, subArrayBuilder, ctorAndArgs);
}
} catch (NoSuchMethodException ex) {
throw new RuntimeException(ex);
}
}
/**
* create a fresh StructuredArray intended to occupy a a given intrinsic field in the containing object,
* at the field described by the supplied intrinsicObjectModel, using the supplied constructor and arguments.
*/
static <T, A extends AbstractStructuredArray<T>> A constructStructuredArrayWithin(
final Object containingObject,
final AbstractIntrinsicObjectModel<A> intrinsicObjectModel,
AbstractStructuredArrayBuilder<A, T> arrayBuilder)
throws InstantiationException, IllegalAccessException, InvocationTargetException {
ConstructionContext context = new ConstructionContext(arrayBuilder.getContextCookie());
ConstructorMagic constructorMagic = getConstructorMagic();
constructorMagic.setConstructionArgs(arrayBuilder, context);
try {
constructorMagic.setActive(true);
return AbstractStructuredArray.constructStructuredArrayWithin(
containingObject,
intrinsicObjectModel,
arrayBuilder.getArrayModel(),
arrayBuilder.getArrayCtorAndArgs().getConstructor(),
arrayBuilder.getArrayCtorAndArgs().getArgs());
} finally {
constructorMagic.setActive(false);
}
}
//
//
// Shallow copy support:
//
//
private static Field[] removeStaticFields(final Field[] declaredFields) {
int staticFieldCount = 0;
for (final Field field : declaredFields) {
if (isStatic(field.getModifiers())) {
staticFieldCount++;
}
}
final Field[] instanceFields = new Field[declaredFields.length - staticFieldCount];
int i = 0;
for (final Field field : declaredFields) {
if (!isStatic(field.getModifiers())) {
instanceFields[i++] = field;
}
}
return instanceFields;
}
private boolean containsFinalQualifiedFields(final Field[] fields) {
for (final Field field : fields) {
if (isFinal(field.getModifiers())) {
return true;
}
}
return false;
}
private static void shallowCopy(final Object src, final Object dst, final Field[] fields) {
try {
for (final Field field : fields) {
field.set(dst, field.get(src));
}
} catch (final IllegalAccessException shouldNotHappen) {
throw new RuntimeException(shouldNotHappen);
}
}
private static void reverseShallowCopy(final Object src, final Object dst, final Field[] fields) {
try {
for (int i = fields.length - 1; i >= 0; i--) {
final Field field = fields[i];
field.set(dst, field.get(src));
}
} catch (final IllegalAccessException shouldNotHappen) {
throw new RuntimeException(shouldNotHappen);
}
}
/**
* Shallow copy a region of element object contents from one array to the other.
* <p>
* shallowCopy will copy all fields from each of the source elements to the corresponding fields in each
* of the corresponding destination elements. If the same array is both the src and dst then the copy will
* happen as if a temporary intermediate array was used.
*
* @param src array to copy
* @param srcOffset offset index in src where the region begins
* @param dst array into which the copy should occur
* @param dstOffset offset index in the dst where the region begins
* @param count of structure elements to copy
* @param <S> The class of the arrays
* @param <T> The class of the array elements
* @throws IllegalArgumentException if the source and destination array element types are not identical, or if
* the source or destination arrays have nested StructuredArrays within them, or if final fields are discovered
* and all allowFinalFieldOverwrite is not true.
*/
static <S extends AbstractStructuredArray<T>, T> void _shallowCopy(
final S src,
final long srcOffset,
final S dst,
final long dstOffset,
final long count) {
_shallowCopy(src, srcOffset, dst, dstOffset, count, false);
}
/**
* Shallow copy a region of element object contents from one array to the other.
* <p>
* shallowCopy will copy all fields from each of the source elements to the corresponding fields in each
* of the corresponding destination elements. If the same array is both the src and dst then the copy will
* happen as if a temporary intermediate array was used.
*
* If <code>allowFinalFieldOverwrite</code> is specified as <code>true</code>, even final fields will be copied.
*
* @param src array to copy
* @param srcOffset offset index in src where the region begins
* @param dst array into which the copy should occur
* @param dstOffset offset index in the dst where the region begins
* @param count of structure elements to copy.
* @param allowFinalFieldOverwrite allow final fields to be overwritten during a copy operation.
* @param <S> The class of the arrays
* @param <T> The class of the array elements
* @throws IllegalArgumentException if the source and destination array element types are not identical, or if
* the source or destination arrays have nested StructuredArrays within them, or if final fields are discovered
* and all allowFinalFieldOverwrite is not true.
*/
static <S extends AbstractStructuredArray<T>, T> void _shallowCopy(
final S src,
final long srcOffset,
final S dst,
final long dstOffset,
final long count,
final boolean allowFinalFieldOverwrite) {
if (src.getElementClass() != dst.getElementClass()) {
String msg = String.format("Only objects of the same class can be copied: %s != %s",
src.getClass(), dst.getClass());
throw new IllegalArgumentException(msg);
}
if ((AbstractStructuredArray.class.isAssignableFrom(src.getElementClass()) ||
(AbstractStructuredArray.class.isAssignableFrom(dst.getElementClass())))) {
throw new IllegalArgumentException("shallowCopy only supported for single dimension arrays (with no nested StructuredArrays)");
}
final Field[] fields = src.fields;
if (!allowFinalFieldOverwrite && dst.hasFinalFields) {
throw new IllegalArgumentException("Cannot shallow copy onto final fields");
}
if (((srcOffset + count) < Integer.MAX_VALUE) && ((dstOffset + count) < Integer.MAX_VALUE)) {
// use the (faster) int based get
if (dst == src && (dstOffset >= srcOffset && (dstOffset + count) >= srcOffset)) {
int srcIdx = (int)(srcOffset + count) - 1;
int dstIdx = (int)(dstOffset + count) - 1;
int limit = (int)(srcOffset - 1);
for (; srcIdx > limit; srcIdx--, dstIdx--) {
reverseShallowCopy(src.get(srcIdx), dst.get(dstIdx), fields);
}
} else {
for (int srcIdx = (int)srcOffset, dstIdx = (int)dstOffset, limit = (int)(srcOffset + count);
srcIdx < limit; srcIdx++, dstIdx++) {
shallowCopy(src.get(srcIdx), dst.get(dstIdx), fields);
}
}
} else {
// use the (slower) long based getL
if (dst == src && (dstOffset >= srcOffset && (dstOffset + count) >= srcOffset)) {
for (long srcIdx = srcOffset + count, dstIdx = dstOffset + count, limit = srcOffset - 1;
srcIdx > limit; srcIdx--, dstIdx--) {
reverseShallowCopy(src.get(srcIdx), dst.get(dstIdx), fields);
}
} else {
for (long srcIdx = srcOffset, dstIdx = dstOffset, limit = srcOffset + count;
srcIdx < limit; srcIdx++, dstIdx++) {
shallowCopy(src.get(srcIdx), dst.get(dstIdx), fields);
}
}
}
}
//
//
// ConstructorMagic support:
//
//
private static class ConstructorMagic {
private boolean isActive() {
return active;
}
private void setActive(boolean active) {
this.active = active;
}
private void setConstructionArgs(final AbstractStructuredArrayBuilder arrayBuilder, final ConstructionContext context) {
this.arrayBuilder = arrayBuilder;
this.context = context;
}
private AbstractStructuredArrayBuilder getArrayBuilder() {
return arrayBuilder;
}
private ConstructionContext getContext() {
return context;
}
private boolean active = false;
private AbstractStructuredArrayBuilder arrayBuilder;
private ConstructionContext context;
}
private static final ThreadLocal<ConstructorMagic> threadLocalConstructorMagic =
new ThreadLocal<ConstructorMagic>() {
@Override protected ConstructorMagic initialValue() {
return new ConstructorMagic();
}
};
private static ConstructorMagic getConstructorMagic() {
return threadLocalConstructorMagic.get();
}
private static void checkConstructorMagic() {
if (!getConstructorMagic().isActive()) {
throw new IllegalArgumentException(
"StructuredArray<> must not be directly instantiated with a constructor." +
" Use newInstance(...) or a builder instead.");
}
}
}