/*
* Copyright 2006 the original author or authors.
*
* 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 org.springmodules.web.propertyeditors;
import java.beans.PropertyEditor;
import java.lang.reflect.Method;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
/**
* {@link org.springframework.beans.propertyeditors.CustomCollectionEditor} implementation for converting a collection of strings to a collection of objects and vice-versa,
* as defined by the <code>convertElement</code> method. For converting you have to define the following:
* <ul>
* <li><code>dataAccessObject</code>, the object used for converting from text to the actual desired object: it could be a Factory, or a DAO.</li>
* <li><code>dataAccessMethod</code>, the method of the dataAccessObject object to call for converting from text to object.</li>
* <li><code>propertyName</code>, the name of the property which will represent the object string value.</li>
* <li><code>stringConvertor</code>, for converting the string value to be passed to the <code>dataAccessMethod</code>.</li>
* <li><code>stringConvertorOutput</code>, the <code>Class</code> of the object converted by the stringConvertor; if not specified,
* the ReflectivePropertyEditor will try to guess it by directly inspecting the converted object.</li>
* </ul>
* This class is useful for binding collections in SpringMVC: i.e., for binding a collection of customers starting from a collection of customer ids, obtained from some kind of
* selection list; using this class you can automatically convert the collection of customer ids to a collection of actual customer objects.
*
* @author Sergio Bossa
*/
public class ReflectiveCollectionEditor extends CustomCollectionEditor {
private Object dataAccessObject;
private String dataAccessMethod;
private String propertyName;
private PropertyEditor stringConvertor;
private Class stringConvertorOutput;
public ReflectiveCollectionEditor(Class collection) {
super(collection);
}
public ReflectiveCollectionEditor(Class collection, boolean nullAsEmptyCollection) {
super(collection, nullAsEmptyCollection);
}
protected Object convertElement(Object element) {
Object result = null;
// Convert from string to object:
if (element instanceof String) {
String textValue = (String) element;
try {
if (this.stringConvertor == null) {
Method method = this.dataAccessObject.getClass().getMethod(this.dataAccessMethod, new Class[]{String.class});
result = method.invoke(this.dataAccessObject, new Object[] {textValue});
} else {
this.stringConvertor.setAsText(textValue);
Object value = this.stringConvertor.getValue();
Method method = null;
if (value == null && this.stringConvertorOutput == null) {
throw new ReflectiveCollectionEditorException("Cannot convert: " + textValue + ". Unable to determine output class.");
} else if (this.stringConvertorOutput != null) {
method = this.dataAccessObject.getClass().getMethod(this.dataAccessMethod, new Class[]{this.stringConvertorOutput});
} else {
method = this.dataAccessObject.getClass().getMethod(this.dataAccessMethod, new Class[]{value.getClass()});
}
result = method.invoke(this.dataAccessObject, new Object[]{value});
}
} catch(Exception ex) {
throw new ReflectiveCollectionEditorException("An error occurred while executing: " + this.dataAccessMethod, ex);
}
}
// Convert from object to string:
else {
try {
BeanWrapperImpl wrapper = new BeanWrapperImpl(element);
result = wrapper.getPropertyValue(this.propertyName).toString();
} catch(Exception ex) {
throw new ReflectiveCollectionEditorException("An error occurred while getting: " + this.propertyName, ex);
}
}
return result;
}
public String getPropertyName() {
return this.propertyName;
}
public void setPropertyName(String propertyName) {
this.propertyName = propertyName;
}
public Object getDataAccessObject() {
return this.dataAccessObject;
}
public void setDataAccessObject(Object dataAccessObject) {
this.dataAccessObject = dataAccessObject;
}
public String getDataAccessMethod() {
return this.dataAccessMethod;
}
public void setDataAccessMethod(String dataAccessMethod) {
this.dataAccessMethod = dataAccessMethod;
}
public PropertyEditor getStringConvertor() {
return this.stringConvertor;
}
public void setStringConvertor(PropertyEditor stringConvertor) {
this.stringConvertor = stringConvertor;
}
public Class getStringConvertorOutput() {
return this.stringConvertorOutput;
}
public void setStringConvertorOutput(Class stringConvertorOutput) {
this.stringConvertorOutput = stringConvertorOutput;
}
/**
* Overridden for creating a new collection even if the original one is of the same type, because we are interested in converting
* collection elements, not collection themselves.
*/
protected boolean alwaysCreateNewCollection() {
return true;
}
}