/*
* Copyright 2002-2006,2009 The Apache Software Foundation.
*
* Licensed 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.ognl.accessor;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.XWorkException;
import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.ListPropertyAccessor;
import ognl.OgnlException;
import ognl.PropertyAccessor;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* Overrides the list property accessor so in the case of trying
* to add properties of a given bean and the JavaBean is not present,
* this class will create the necessary blank JavaBeans.
*
* @author Gabriel Zimmerman
*/
public class XWorkListPropertyAccessor extends ListPropertyAccessor {
private XWorkCollectionPropertyAccessor _sAcc = new XWorkCollectionPropertyAccessor();
private XWorkConverter xworkConverter;
private ObjectFactory objectFactory;
private ObjectTypeDeterminer objectTypeDeterminer;
private OgnlUtil ognlUtil;
private int autoGrowCollectionLimit = 255;
@Inject(value = "xwork.autoGrowCollectionLimit", required = false)
public void setAutoGrowCollectionLimit(String value) {
this.autoGrowCollectionLimit = Integer.valueOf(value);
}
@Inject("java.util.Collection")
public void setXWorkCollectionPropertyAccessor(PropertyAccessor acc) {
this._sAcc = (XWorkCollectionPropertyAccessor) acc;
}
@Inject
public void setXWorkConverter(XWorkConverter conv) {
this.xworkConverter = conv;
}
@Inject
public void setObjectFactory(ObjectFactory fac) {
this.objectFactory = fac;
}
@Inject
public void setObjectTypeDeterminer(ObjectTypeDeterminer ot) {
this.objectTypeDeterminer = ot;
}
@Inject
public void setOgnlUtil(OgnlUtil util) {
this.ognlUtil = util;
}
@Override
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
if (ReflectionContextState.isGettingByKeyProperty(context)
|| name.equals(XWorkCollectionPropertyAccessor.KEY_PROPERTY_FOR_CREATION)) {
return _sAcc.getProperty(context, target, name);
} else if (name instanceof String) {
return super.getProperty(context, target, name);
}
ReflectionContextState.updateCurrentPropertyPath(context, name);
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
if (name instanceof Number
&& ReflectionContextState.isCreatingNullObjects(context)
&& objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,true)) {
List list = (List) target;
int index = ((Number) name).intValue();
int listSize = list.size();
if (lastClass == null || lastProperty == null) {
return super.getProperty(context, target, name);
}
Class beanClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
if (listSize <= index) {
Object result;
for (int i = listSize; i < index; i++) {
list.add(null);
}
try {
list.add(index, result = objectFactory.buildBean(beanClass, context));
} catch (Exception exc) {
throw new XWorkException(exc);
}
return result;
} else if (list.get(index) == null) {
Object result;
try {
list.set(index, result = objectFactory.buildBean(beanClass, context));
} catch (Exception exc) {
throw new XWorkException(exc);
}
return result;
}
}
return super.getProperty(context, target, name);
}
@Override
public void setProperty(Map context, Object target, Object name, Object value)
throws OgnlException {
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
Class convertToClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, name);
if (name instanceof String && value.getClass().isArray()) {
// looks like the input game in the form of "someList.foo" and
// we are expected to define the index values ourselves.
// So let's do it:
Collection c = (Collection) target;
Object[] values = (Object[]) value;
for (Object v : values) {
try {
Object o = objectFactory.buildBean(convertToClass, context);
ognlUtil.setValue((String) name, context, o, v);
c.add(o);
} catch (Exception e) {
throw new OgnlException("Error converting given String values for Collection.", e);
}
}
// we don't want to do the normal list property setting now, since we've already done the work
// just return instead
return;
}
Object realValue = getRealValue(context, value, convertToClass);
if (target instanceof List && name instanceof Number) {
//make sure there are enough spaces in the List to set
List list = (List) target;
int listSize = list.size();
int count = ((Number) name).intValue();
if(count > autoGrowCollectionLimit)
throw new OgnlException("Error auto growing collection size to " + count + " which limited to "
+ autoGrowCollectionLimit);
if (count >= listSize) {
for (int i = listSize; i <= count; i++) {
list.add(null);
}
}
}
super.setProperty(context, target, name, realValue);
}
private Object getRealValue(Map context, Object value, Class convertToClass) {
if (value == null || convertToClass == null) {
return value;
}
return xworkConverter.convertValue(context, value, convertToClass);
}
}