/*
* 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.conversion.ObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
import ognl.MapPropertyAccessor;
import ognl.OgnlException;
import java.util.Map;
/**
* Implementation of PropertyAccessor that sets and gets properties by storing and looking
* up values in Maps.
*
* @author Gabriel Zimmerman
*/
public class XWorkMapPropertyAccessor extends MapPropertyAccessor {
private static final Logger LOG = LoggerFactory.getLogger(XWorkMapPropertyAccessor.class);
private static final String[] INDEX_ACCESS_PROPS = new String[]
{"size", "isEmpty", "keys", "values"};
private XWorkConverter xworkConverter;
private ObjectFactory objectFactory;
private ObjectTypeDeterminer objectTypeDeterminer;
@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;
}
@Override
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
if (LOG.isDebugEnabled()) {
LOG.debug("Entering getProperty ("+context+","+target+","+name+")");
}
ReflectionContextState.updateCurrentPropertyPath(context, name);
// if this is one of the regular index access
// properties then just let the superclass deal with the
// get.
if (name instanceof String && contains(INDEX_ACCESS_PROPS, (String) name)) {
return super.getProperty(context, target, name);
}
Object result = null;
try{
result = super.getProperty(context, target, name);
} catch(ClassCastException ex){
}
if (result == null) {
//find the key class and convert the name to that class
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
if (lastClass == null || lastProperty == null) {
return super.getProperty(context, target, name);
}
Class keyClass = objectTypeDeterminer
.getKeyClass(lastClass, lastProperty);
if (keyClass == null) {
keyClass = java.lang.String.class;
}
Object key = getKey(context, name);
Map map = (Map) target;
result = map.get(key);
if (result == null &&
context.get(ReflectionContextState.CREATE_NULL_OBJECTS) != null
&& objectTypeDeterminer.shouldCreateIfNew(lastClass,lastProperty,target,null,false)) {
Class valueClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, key);
try {
result = objectFactory.buildBean(valueClass, context);
map.put(key, result);
} catch (Exception exc) {
}
}
}
return result;
}
/**
* @param array
* @param name
*/
private boolean contains(String[] array, String name) {
for (String anArray : array) {
if (anArray.equals(name)) {
return true;
}
}
return false;
}
@Override
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
if (LOG.isDebugEnabled()) {
LOG.debug("Entering setProperty("+context+","+target+","+name+","+value+")");
}
Object key = getKey(context, name);
Map map = (Map) target;
map.put(key, getValue(context, value));
}
private Object getValue(Map context, Object value) {
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
if (lastClass == null || lastProperty == null) {
return value;
}
Class elementClass = objectTypeDeterminer.getElementClass(lastClass, lastProperty, null);
if (elementClass == null) {
return value; // nothing is specified, we assume it will be the value passed in.
}
return xworkConverter.convertValue(context, value, elementClass);
}
private Object getKey(Map context, Object name) {
Class lastClass = (Class) context.get(XWorkConverter.LAST_BEAN_CLASS_ACCESSED);
String lastProperty = (String) context.get(XWorkConverter.LAST_BEAN_PROPERTY_ACCESSED);
if (lastClass == null || lastProperty == null) {
// return java.lang.String.class;
// commented out the above -- it makes absolutely no sense for when setting basic maps!
return name;
}
Class keyClass = objectTypeDeterminer.getKeyClass(lastClass, lastProperty);
if (keyClass == null) {
keyClass = java.lang.String.class;
}
return xworkConverter.convertValue(context, name, keyClass);
}
}