All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.beanutils2.LazyDynaList Maven / Gradle / Ivy

Go to download

Apache Commons BeanUtils provides an easy-to-use but flexible wrapper around reflection and introspection.

The newest version!
/*
 * 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 org.apache.commons.beanutils2;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

/**
 * 

Lazy DynaBean List.

* *

There are two main purposes for this class:

*
    *
  • To provide Lazy List behavior - automatically * growing and populating the {@code List} * with either {@code DynaBean, java.util.Map} * or POJO Beans.
  • *
  • To provide a straight forward way of putting a Collection * or Array into the lazy list and a straight forward * way to get it out again at the end.
  • *
* *

All elements added to the List are stored as {@code DynaBean}'s:

*
    *
  • {@code java.util.Map elements are "wrapped" in a LazyDynaMap}.
  • *
  • POJO Bean elements are "wrapped" in a {@code WrapDynaBean}.
  • *
  • {@code DynaBean}'s are stored un-changed.
  • *
* *

{@code toArray()}

*

The {@code toArray()} method returns an array of the * elements of the appropriate type. If the {@code LazyDynaList} * is populated with {@code java.util.Map} objects a * {@code Map[]} array is returned. * If the list is populated with POJO Beans an appropriate * array of the POJO Beans is returned. Otherwise a {@code DynaBean[]} * array is returned. *

* *

{@code toDynaBeanArray()}

*

The {@code toDynaBeanArray()} method returns a * {@code DynaBean[]} array of the elements in the List. *

* *

N.B.All the elements in the List must be the * same type. If the {@code DynaClass or Class} * of the {@code LazyDynaList}'s elements is * not specified, then it will be automatically set to the type * of the first element populated. *

* *

Example 1

*

If you have an array of {@code java.util.Map[]} - you can put that into * a {@code LazyDynaList}.

* *

 *    TreeMap[] myArray = .... // your Map[]
 *    List lazyList = new LazyDynaList(myArray);
 * 
* *

New elements of the appropriate Map type are * automatically populated:

* *

 *    // get(index) automatically grows the list
 *    DynaBean newElement = (DynaBean)lazyList.get(lazyList.size());
 *    newElement.put("someProperty", "someValue");
 * 
* *

Once you've finished you can get back an Array of the * elements of the appropriate type:

* *

 *    // Retrieve the array from the list
 *    TreeMap[] myArray = (TreeMap[])lazyList.toArray());
 * 
* * *

Example 2

*

Alternatively you can create an empty List and * specify the Class for List's elements. The LazyDynaList * uses the Class to automatically populate elements:

* *

 *    // e.g. For Maps
 *    List lazyList = new LazyDynaList(TreeMap.class);
 *
 *    // e.g. For POJO Beans
 *    List lazyList = new LazyDynaList(MyPojo.class);
 *
 *    // e.g. For DynaBeans
 *    List lazyList = new LazyDynaList(MyDynaBean.class);
 * 
* *

Example 3

*

Alternatively you can create an empty List and specify the * DynaClass for List's elements. The LazyDynaList uses * the DynaClass to automatically populate elements:

* *

 *    // e.g. For Maps
 *    DynaClass dynaClass = new LazyDynaMap(new HashMap());
 *    List lazyList = new LazyDynaList(dynaClass);
 *
 *    // e.g. For POJO Beans
 *    DynaClass dynaClass = (new WrapDynaBean(myPojo)).getDynaClass();
 *    List lazyList = new LazyDynaList(dynaClass);
 *
 *    // e.g. For DynaBeans
 *    DynaClass dynaClass = new BasicDynaClass(properties);
 *    List lazyList = new LazyDynaList(dynaClass);
 * 
* *

N.B. You may wonder why control the type * using a {@code DynaClass rather than the Class} * as in the previous example - the reason is that some {@code DynaBean} * implementations don't have a default empty constructor and * therefore need to be instantiated using the {@code DynaClass.newInstance()} * method.

* *

Example 4

*

A slight variation - set the element type using either * the {@code setElementType(Class)} method or the * {@code setElementDynaClass(DynaClass)} method - then populate * with the normal {@code java.util.List} methods(i.e. * {@code add(), addAll() or set()}).

* *

 *    // Create a new LazyDynaList (100 element capacity)
 *    LazyDynaList lazyList = new LazyDynaList(100);
 *
 *    // Either Set the element type...
 *    lazyList.setElementType(TreeMap.class);
 *
 *    // ...or the element DynaClass...
 *    lazyList.setElementDynaClass(new MyCustomDynaClass());
 *
 *    // Populate from a collection
 *    lazyList.addAll(myCollection);
 *
 * 
* * @since 1.8.0 */ public class LazyDynaList extends ArrayList { private static final long serialVersionUID = 1L; /** * The DynaClass of the List's elements. */ private DynaClass elementDynaClass; /** * The WrapDynaClass if the List's contains * POJO Bean elements. * * N.B. WrapDynaClass isn't serializable, which * is why its stored separately in a * transient instance variable. */ private transient WrapDynaClass wrapDynaClass; /** * The type of the List's elements. */ private Class elementType; /** * The DynaBean type of the List's elements. */ private Class elementDynaBeanType; /** * Default Constructor. */ public LazyDynaList() { } /** * Constructs a LazyDynaList with the * specified capacity. * * @param capacity The initial capacity of the list. */ public LazyDynaList(final int capacity) { super(capacity); } /** * Constructs a LazyDynaList with a * specified DynaClass for its elements. * * @param elementDynaClass The DynaClass of the List's elements. */ public LazyDynaList(final DynaClass elementDynaClass) { setElementDynaClass(elementDynaClass); } /** * Constructs a LazyDynaList with a * specified type for its elements. * * @param elementType The Type of the List's elements. */ public LazyDynaList(final Class elementType) { setElementType(elementType); } /** * Constructs a LazyDynaList populated with the * elements of a Collection. * * @param collection The Collection to populate the List from. */ public LazyDynaList(final Collection collection) { super(collection.size()); addAll(collection); } /** * Constructs a LazyDynaList populated with the * elements of an Array. * * @param array The Array to populate the List from. */ public LazyDynaList(final Object[] array) { super(array.length); this.addAll(Arrays.asList(array)); } /** *

Insert an element at the specified index position.

* *

If the index position is greater than the current * size of the List, then the List is automatically * grown to the appropriate size.

* * @param index The index position to insert the new element. * @param element The new element to add. */ @Override public void add(final int index, final Object element) { final DynaBean dynaBean = transform(element); growList(index); super.add(index, dynaBean); } /** *

Add an element to the List.

* * @param element The new element to add. * @return true. */ @Override public boolean add(final Object element) { final DynaBean dynaBean = transform(element); return super.add(dynaBean); } /** *

Add all the elements from a Collection to the list. * * @param collection The Collection of new elements. * @return true if elements were added. */ @Override public boolean addAll(final Collection collection) { if (collection == null || collection.isEmpty()) { return false; } ensureCapacity(size() + collection.size()); for (final Object e : collection) { add(e); } return true; } /** *

Insert all the elements from a Collection into the * list at a specified position. * *

If the index position is greater than the current * size of the List, then the List is automatically * grown to the appropriate size.

* * @param collection The Collection of new elements. * @param index The index position to insert the new elements at. * @return true if elements were added. */ @Override public boolean addAll(final int index, final Collection collection) { if (collection == null || collection.isEmpty()) { return false; } ensureCapacity((Math.max(index, size())) + collection.size()); // Call "transform" with first element, before // List is "grown" to ensure the correct DynaClass // is set. if (isEmpty()) { transform(collection.iterator().next()); } growList(index); int currentIndex = index; for (final Object e : collection) { add(currentIndex++, e); } return true; } /** *

Return the element at the specified position.

* *

If the position requested is greater than the current * size of the List, then the List is automatically * grown (and populated) to the appropriate size.

* * @param index The index position to insert the new elements at. * @return The element at the specified position. */ @Override public Object get(final int index) { growList(index + 1); return super.get(index); } /** *

Set the element at the specified position.

* *

If the position requested is greater than the current * size of the List, then the List is automatically * grown (and populated) to the appropriate size.

* * @param index The index position to insert the new element at. * @param element The new element. * @return The new element. */ @Override public Object set(final int index, final Object element) { final DynaBean dynaBean = transform(element); growList(index + 1); return super.set(index, dynaBean); } /** *

Converts the List to an Array.

* *

The type of Array created depends on the contents * of the List:

*
    *
  • If the List contains only LazyDynaMap type elements * then a java.util.Map[] array will be created.
  • *
  • If the List contains only elements which are * "wrapped" DynaBeans then an Object[] of the most * suitable type will be created.
  • *
  • ...otherwise a DynaBean[] will be created.
  • *
* * @return An Array of the elements in this List. */ @Override public Object[] toArray() { if (isEmpty() && elementType == null) { return LazyDynaBean.EMPTY_LAZY_DYNA_BEAN_ARRAY; } final Object[] array = (Object[])Array.newInstance(elementType, size()); for (int i = 0; i < size(); i++) { if (Map.class.isAssignableFrom(elementType)) { array[i] = ((LazyDynaMap)get(i)).getMap(); } else if (DynaBean.class.isAssignableFrom(elementType)) { array[i] = get(i); } else { array[i] = ((WrapDynaBean)get(i)).getInstance(); } } return array; } /** *

Converts the List to an Array of the specified type.

* * @param The type of the array elements * @param model The model for the type of array to return * @return An Array of the elements in this List. */ @Override public T[] toArray(final T[] model) { final Class arrayType = model.getClass().getComponentType(); if (DynaBean.class.isAssignableFrom(arrayType) || isEmpty() && elementType == null) { return super.toArray(model); } if (arrayType.isAssignableFrom(elementType)) { T[] array; if (model.length >= size()) { array = model; } else { @SuppressWarnings("unchecked") final // This is safe because we know the element type T[] tempArray = (T[]) Array.newInstance(arrayType, size()); array = tempArray; } for (int i = 0; i < size(); i++) { Object elem; if (Map.class.isAssignableFrom(elementType)) { elem = ((LazyDynaMap) get(i)).getMap(); } else if (DynaBean.class.isAssignableFrom(elementType)) { elem = get(i); } else { elem = ((WrapDynaBean) get(i)).getInstance(); } Array.set(array, i, elem); } return array; } throw new IllegalArgumentException("Invalid array type: " + arrayType.getName() + " - not compatible with '" + elementType.getName()); } /** *

Converts the List to an DynaBean Array.

* * @return A DynaBean[] of the elements in this List. */ public DynaBean[] toDynaBeanArray() { if (isEmpty() && elementDynaBeanType == null) { return LazyDynaBean.EMPTY_LAZY_DYNA_BEAN_ARRAY; } final DynaBean[] array = (DynaBean[])Array.newInstance(elementDynaBeanType, size()); for (int i = 0; i < size(); i++) { array[i] = (DynaBean)get(i); } return array; } /** *

Set the element Type and DynaClass.

* * @param elementType The type of the elements. * @throws IllegalArgumentException if the List already * contains elements or the DynaClass is null. */ public void setElementType(final Class elementType) { if (elementType == null) { throw new IllegalArgumentException("Element Type is missing"); } final boolean changeType = this.elementType != null && !this.elementType.equals(elementType); if (changeType && !isEmpty()) { throw new IllegalStateException("Element Type cannot be reset"); } this.elementType = elementType; // Create a new object of the specified type Object object = null; try { object = elementType.newInstance(); } catch (final Exception e) { throw new IllegalArgumentException("Error creating type: " + elementType.getName() + " - " + e); } // Create a DynaBean DynaBean dynaBean = null; if (Map.class.isAssignableFrom(elementType)) { dynaBean = createDynaBeanForMapProperty(object); this.elementDynaClass = dynaBean.getDynaClass(); } else if (DynaBean.class.isAssignableFrom(elementType)) { dynaBean = (DynaBean)object; this.elementDynaClass = dynaBean.getDynaClass(); } else { dynaBean = new WrapDynaBean(object); this.wrapDynaClass = (WrapDynaClass)dynaBean.getDynaClass(); } this.elementDynaBeanType = dynaBean.getClass(); // Re-calculate the type if (WrapDynaBean.class.isAssignableFrom(elementDynaBeanType )) { this.elementType = ((WrapDynaBean)dynaBean).getInstance().getClass(); } else if (LazyDynaMap.class.isAssignableFrom(elementDynaBeanType )) { this.elementType = ((LazyDynaMap)dynaBean).getMap().getClass(); } } /** *

Set the element Type and DynaClass.

* * @param elementDynaClass The DynaClass of the elements. * @throws IllegalArgumentException if the List already * contains elements or the DynaClass is null. */ public void setElementDynaClass(final DynaClass elementDynaClass) { if (elementDynaClass == null) { throw new IllegalArgumentException("Element DynaClass is missing"); } if (!isEmpty()) { throw new IllegalStateException("Element DynaClass cannot be reset"); } // Try to create a new instance of the DynaBean try { final DynaBean dynaBean = elementDynaClass.newInstance(); this.elementDynaBeanType = dynaBean.getClass(); if (WrapDynaBean.class.isAssignableFrom(elementDynaBeanType)) { this.elementType = ((WrapDynaBean)dynaBean).getInstance().getClass(); this.wrapDynaClass = (WrapDynaClass)elementDynaClass; } else if (LazyDynaMap.class.isAssignableFrom(elementDynaBeanType)) { this.elementType = ((LazyDynaMap)dynaBean).getMap().getClass(); this.elementDynaClass = elementDynaClass; } else { this.elementType = dynaBean.getClass(); this.elementDynaClass = elementDynaClass; } } catch (final Exception e) { throw new IllegalArgumentException( "Error creating DynaBean from " + elementDynaClass.getClass().getName() + " - " + e); } } /** *

Automatically grown the List * to the appropriate size, populating with * DynaBeans.

* * @param requiredSize the required size of the List. */ private void growList(final int requiredSize) { if (requiredSize < size()) { return; } ensureCapacity(requiredSize + 1); for (int i = size(); i < requiredSize; i++) { final DynaBean dynaBean = transform(null); super.add(dynaBean); } } /** *

Transform the element into a DynaBean:

* *