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

com.feilong.lib.beanutils.LazyDynaList Maven / Gradle / Ivy

Go to download

feilong is a suite of core and expanded libraries that include utility classes, http, excel,cvs, io classes, and much much more.

There is a newer version: 4.0.8
Show 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 com.feilong.lib.beanutils;

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

import org.apache.commons.beanutils.DynaBean;

/**
 * 

Lazy DynaBean List.

* *

* There are two main purposes for this class: *

*
    *
  • To provide Lazy List behaviour - automatically * growing and populating the List * with either 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 DynaBean's: *

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

toArray()

*

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

* *

toDynaBeanArray()

*

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

* *

* N.B.All the elements in the List must be the * same type. If the DynaClass or Class * of the 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 java.util.Map[] - you can put that into * a 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 DynaClass rather than the Class * as in the previous example - the reason is that some DynaBean * implementations don't have a default empty constructor and * therefore need to be instantiated using the DynaClass.newInstance() * method. *

* *

Example 4

*

* A slight variation - set the element type using either * the setElementType(Class) method or the * setElementDynaClass(DynaClass) method - then populate * with the normal java.util.List methods(i.e. * 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);
 *
 * 
 * 
* * @version $Id$ * @since 1.8.0 */ public class LazyDynaList extends ArrayList{ /** * */ private static final long serialVersionUID = -315007478508532067L; /** * 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 serlializable, 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; // ------------------- Constructors ------------------------------ /** * Default Constructor. */ public LazyDynaList(){ super(); } /** * Construct a LazyDynaList with the * specified capacity. * * @param capacity * The initial capacity of the list. */ public LazyDynaList(final int capacity){ super(capacity); } /** * Construct a LazyDynaList with a * specified DynaClass for its elements. * * @param elementDynaClass * The DynaClass of the List's elements. */ public LazyDynaList(final DynaClass elementDynaClass){ super(); setElementDynaClass(elementDynaClass); } /** * Construct a LazyDynaList with a * specified type for its elements. * * @param elementType * The Type of the List's elements. */ public LazyDynaList(final Class elementType){ super(); setElementType(elementType); } /** * Construct 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); } /** * Construct 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); for (Object element : array){ add(element); } } // ------------------- java.util.List Methods -------------------- /** *

* 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.size() == 0){ 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.size() == 0){ return false; } ensureCapacity((index > size() ? index : size()) + collection.size()); // Call "transform" with first element, before // List is "grown" to ensure the correct DynaClass // is set. if (size() == 0){ 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 (size() == 0 && elementType == null){ return new LazyDynaBean[0]; } 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)) || (size() == 0 && 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()); } // ------------------- Public Methods ---------------------------- /** *

    * Converts the List to an DynaBean Array. *

    * * @return A DynaBean[] of the elements in this List. */ public DynaBean[] toDynaBeanArray(){ if (size() == 0 && elementDynaBeanType == null){ return new LazyDynaBean[0]; } 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 && size() > 0){ 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 (size() > 0){ 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); } } // ------------------- Private Methods --------------------------- /** *

    * 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: *

    * *
      *
    • Map elements are turned into LazyDynaMap's.
    • *
    • POJO Beans are "wrapped" in a WrapDynaBean.
    • *
    • DynaBeans are unchanged.
    • * * * @param element * The element to transformed. * @param The * DynaBean to store in the List. */ private DynaBean transform(final Object element){ DynaBean dynaBean = null; Class newDynaBeanType = null; Class newElementType = null; // Create a new element if (element == null){ // Default Types to LazyDynaBean // if not specified if (elementType == null){ setElementDynaClass(new LazyDynaClass()); } // Get DynaClass (restore WrapDynaClass lost in serialization) if (getDynaClass() == null){ setElementType(elementType); } // Create a new DynaBean try{ dynaBean = getDynaClass().newInstance(); newDynaBeanType = dynaBean.getClass(); }catch (final Exception e){ throw new IllegalArgumentException("Error creating DynaBean: " + getDynaClass().getClass().getName() + " - " + e); } }else{ // Transform Object to a DynaBean newElementType = element.getClass(); if (Map.class.isAssignableFrom(element.getClass())){ dynaBean = createDynaBeanForMapProperty(element); }else if (DynaBean.class.isAssignableFrom(element.getClass())){ dynaBean = (DynaBean) element; }else{ dynaBean = new WrapDynaBean(element); } newDynaBeanType = dynaBean.getClass(); } // Re-calculate the element type newElementType = dynaBean.getClass(); if (WrapDynaBean.class.isAssignableFrom(newDynaBeanType)){ newElementType = ((WrapDynaBean) dynaBean).getInstance().getClass(); }else if (LazyDynaMap.class.isAssignableFrom(newDynaBeanType)){ newElementType = ((LazyDynaMap) dynaBean).getMap().getClass(); } // Check the new element type, matches all the // other elements in the List if (elementType != null && !newElementType.equals(elementType)){ throw new IllegalArgumentException("Element Type " + newElementType + " doesn't match other elements " + elementType); } return dynaBean; } /** * Creates a new {@code LazyDynaMap} object for the given property value. * * @param value * the property value * @return the newly created {@code LazyDynaMap} */ private LazyDynaMap createDynaBeanForMapProperty(final Object value){ @SuppressWarnings("unchecked") final // map properties are always stored as Map Map valueMap = (Map) value; return new LazyDynaMap(valueMap); } /** * Return the DynaClass. */ private DynaClass getDynaClass(){ return (elementDynaClass == null ? wrapDynaClass : elementDynaClass); } }