blob: 6243c109757db809586321864ecd11af2b0c2635 [file] [log] [blame]
/*
* 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.opensymphony.xwork2.conversion.impl;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.conversion.TypeConverter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* A simple list that guarantees that inserting and retrieving objects will always work regardless
* of the current size of the list. Upon insertion, type conversion is also performed if necessary.
* Empty beans will be created to fill the gap between the current list size and the requested index
* using ObjectFactory's {@link ObjectFactory#buildBean(Class,java.util.Map) buildBean} method.
*
* @author Patrick Lightbody
*/
public class XWorkList extends ArrayList {
private static final Logger LOG = LogManager.getLogger(XWorkConverter.class);
private Class clazz;
public XWorkList(Class clazz) {
this.clazz = clazz;
}
public XWorkList(Class clazz, int initialCapacity) {
super(initialCapacity);
this.clazz = clazz;
}
/**
* <p>
* Inserts the specified element at the specified position in this list. Shifts the element
* currently at that position (if any) and any subsequent elements to the right (adds one to
* their indices).
* </p>
*
* <p>
* This method is guaranteed to work since it will create empty beans to fill the gap between
* the current list size and the requested index to enable the element to be set. This method
* also performs any necessary type conversion.
* </p>
*
* @param index index at which the specified element is to be inserted.
* @param element element to be inserted.
*/
@Override
public void add(int index, Object element) {
if (index >= this.size()) {
get(index);
}
element = convert(element);
super.add(index, element);
}
/**
* <p>
* Appends the specified element to the end of this list.
* </p>
*
* <p>
* This method performs any necessary type conversion.
* </p>
*
* @param element element to be appended to this list.
* @return <tt>true</tt> (as per the general contract of Collection.add).
*/
@Override
public boolean add(Object element) {
element = convert(element);
return super.add(element);
}
/**
* <p>
* Appends all of the elements in the specified Collection to the end of this list, in the order
* that they are returned by the specified Collection's Iterator. The behavior of this
* operation is undefined if the specified Collection is modified while the operation is in
* progress. (This implies that the behavior of this call is undefined if the specified
* Collection is this list, and this list is nonempty.)
* </p>
*
* <p>
* This method performs any necessary type conversion.
* </p>
*
* @param collection the elements to be inserted into this list.
* @return <tt>true</tt> if this list changed as a result of the call.
* @throws NullPointerException if the specified collection is null.
*/
@Override
public boolean addAll(Collection collection) {
if (collection == null) {
throw new NullPointerException("Collection to add is null");
}
for (Object nextElement : collection) {
add(nextElement);
}
return true;
}
/**
* <p>
* Inserts all of the elements in the specified Collection into this list, starting at the
* specified position. Shifts the element currently at that position (if any) and any
* subsequent elements to the right (increases their indices). The new elements will appear in
* the list in the order that they are returned by the specified Collection's iterator.
* </p>
*
* <p>
* This method is guaranteed to work since it will create empty beans to fill the gap between
* the current list size and the requested index to enable the element to be set. This method
* also performs any necessary type conversion.
* </p>
*
* @param index index at which to insert first element from the specified collection.
* @param collection elements to be inserted into this list.
* @return <tt>true</tt> if this list changed as a result of the call.
*/
@Override
public boolean addAll(int index, Collection collection) {
if (collection == null) {
throw new NullPointerException("Collection to add is null");
}
boolean trim = false;
if (index >= this.size()) {
trim = true;
}
for (Iterator it = collection.iterator(); it.hasNext(); index++) {
add(index, it.next());
}
if (trim) {
remove(this.size() - 1);
}
return true;
}
/**
* <p>
* Returns the element at the specified position in this list.
* </p>
*
* <p>
* An object is guaranteed to be returned since it will create empty beans to fill the gap
* between the current list size and the requested index.
* </p>
*
* @param index index of element to return.
* @return the element at the specified position in this list.
*/
@Override
public synchronized Object get(int index) {
while (index >= this.size()) {
try {
this.add(getObjectFactory().buildBean(clazz, ActionContext.getContext().getContextMap()));
} catch (Exception e) {
throw new StrutsException(e);
}
}
return super.get(index);
}
private ObjectFactory getObjectFactory() {
return ActionContext.getContext().getInstance(ObjectFactory.class);
}
/**
* <p>
* Replaces the element at the specified position in this list with the specified element.
* </p>
*
* <p>
* This method is guaranteed to work since it will create empty beans to fill the gap between
* the current list size and the requested index to enable the element to be set. This method
* also performs any necessary type conversion.
* </p>
*
* @param index index of element to replace.
* @param element element to be stored at the specified position.
* @return the element previously at the specified position.
*/
@Override
public Object set(int index, Object element) {
if (index >= this.size()) {
get(index);
}
element = convert(element);
return super.set(index, element);
}
private Object convert(Object element) {
if ((element != null) && !clazz.isAssignableFrom(element.getClass())) {
// convert to correct type
LOG.debug("Converting from {} to {}", element.getClass().getName(), clazz.getName());
TypeConverter converter = getTypeConverter();
Map<String, Object> context = ActionContext.getContext().getContextMap();
element = converter.convertValue(context, null, null, null, element, clazz);
}
return element;
}
private TypeConverter getTypeConverter() {
return ActionContext.getContext().getContainer().getInstance(XWorkConverter.class);
}
@Override
public boolean contains(Object element) {
element = convert(element);
return super.contains(element);
}
}