/*
* Copyright 2009-2011 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.jdal.vaadin.ui.form;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import com.vaadin.data.Container;
import com.vaadin.data.Item;
import com.vaadin.data.util.BeanItem;
import com.vaadin.ui.Component;
import com.vaadin.ui.DefaultFieldFactory;
import com.vaadin.ui.Field;
/**
* Configurable FieldFactory, to configure fieldFactories friendly from Spring context bean definition
* files.
*
* @author Jose Luis Martin - (jlm@joseluismartin.info)
* @since 1.1
*/
public class ConfigurableFieldFactory extends DefaultFieldFactory {
/** log */
private static final Log log = LogFactory.getLog(ConfigurableFieldFactory.class);
/** class to class field map */
private Map<Class<?>, Class<?extends Field>> classFieldMap = Collections.synchronizedMap(
new HashMap<Class<?>, Class<?extends Field>>());
/** propertyId to FieldBuilder map */
private Map<Object, FieldBuilder> idBuilderMap = Collections.synchronizedMap(
new HashMap<Object, FieldBuilder>());
/** propertyId to class map */
private Map<Object, Class<?extends Field>> idClassMap = Collections.synchronizedMap(
new HashMap<Object, Class<?extends Field>>());
/** class to field builder map */
private Map<Class<?>, FieldBuilder> classBuilderMap = Collections.synchronizedMap(
new HashMap<Class<?>, FieldBuilder>());
/** fiedl processor list */
private List<FieldProcessor> fieldProcessors = new ArrayList<FieldProcessor>();
/**
* {@inheritDoc}
*/
@Override
public Field createField(Container container, Object itemId, Object propertyId, Component uiContext) {
return createField(container.getItem(itemId), propertyId, uiContext);
}
/**
* @param f
*/
protected void applyFieldProcessors(Field f, Object propertyId) {
if (f != null) {
for (FieldProcessor fp : fieldProcessors)
fp.processField(f, propertyId);
}
}
/**
* {@inheritDoc}
*/
@Override
public Field createField(Item item, Object propertyId, Component uiContext) {
BeanItem<?> beanItem = (BeanItem<?>) item;
Field f = getField(propertyId, beanItem.getBean().getClass());
if (f != null) {
f.setCaption(createCaptionByPropertyId(propertyId));
}
else {
// fail back to default
f = super.createField(item, propertyId, uiContext);
}
applyFieldProcessors(f, propertyId);
return f;
}
/**
* Try to find a field. It will tray the four configured maps in order:
* <ol>
* <li> propertyId to FieldBuilder map.</li>
* <li> propertyId to Field map.</li>
* <li> propertyClass to FieldBuilder map.</li>
* <li> propertyClass to Field map.</li>
* </ol>
* @param propertyId the propertyId
* @param clazz the bean class holding the propertyId
* @return Field or null if none configured
*/
@SuppressWarnings("unchecked")
protected Field getField(Object propertyId, Class<?> clazz) {
// try id to builder map
FieldBuilder builder = idBuilderMap.get(propertyId);
if (builder != null) {
if (log.isDebugEnabled())
log.debug("Found FieldBuilder in idBuilderMap: [" + builder.getClass().getSimpleName() +"]");
return builder.build(clazz, (String) propertyId);
}
// try id to class Map
Class<?extends Field> fieldClass = idClassMap.get(propertyId);
if (fieldClass != null) {
if (log.isDebugEnabled())
log.debug("Found FieldBuilder in idClassMap: [" + fieldClass.getSimpleName() +"]");
return BeanUtils.instantiate(fieldClass);
}
// try class to builder map
Class<?> propertyClass = BeanUtils.getPropertyDescriptor(clazz, (String) propertyId).getPropertyType();
builder = (FieldBuilder) findByClass(propertyClass, classBuilderMap);
if (builder != null) {
if (log.isDebugEnabled())
log.debug("Found FieldBuilder in classBuilderMap: [" + builder.getClass().getSimpleName() +"]");
return builder.build(clazz, (String) propertyId);
}
// try class to field map
fieldClass = (Class<? extends Field>) findByClass(propertyClass, classFieldMap);
if (fieldClass != null) {
if (log.isDebugEnabled())
log.debug("Found FieldBuilder in classFieldMap: [" + fieldClass.getSimpleName() +"]");
return BeanUtils.instantiate(fieldClass);
}
log.debug("Not found field for propertyId: " + propertyId);
return null;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
protected Object findByClass(Class<?> clazz, Map<Class<?>, ?> map) {
Object target;
target = map.get(clazz);
if (target == null) { // try with superclasses
List superclasses = ClassUtils.getAllSuperclasses(clazz);
superclasses.addAll(ClassUtils.getAllInterfaces(clazz));
Iterator iter = superclasses.iterator();
while (iter.hasNext() && target == null) {
target = map.get(iter.next());
}
}
return target;
}
/**
* @return the fieldMap
*/
public Map<Class<?>, Class<? extends Field>> getClassFieldMap() {
return classFieldMap;
}
/**
* @param fieldMap the fieldMap to set
*/
public void setClassFieldMap(Map<Class<?>, Class<? extends Field>> fieldMap) {
this.classFieldMap.clear();
this.classFieldMap.putAll(fieldMap);
}
/**
* @return the fieldProcessors
*/
public List<FieldProcessor> getFieldProcessors() {
return fieldProcessors;
}
/**
* @param fieldProcessors the fieldProcessors to set
*/
public void setFieldProcessors(List<FieldProcessor> fieldProcessors) {
this.fieldProcessors = fieldProcessors;
}
/**
* @param idBuilderMap the builderMap to set
*/
public void setIdBuilderMap(Map<Object, FieldBuilder> idBuilderMap) {
this.idBuilderMap.clear();
this.idBuilderMap.putAll(idBuilderMap);
}
/**
* @return the builderMap
*/
public Map<Object, FieldBuilder> getIdBuilderMap() {
return idBuilderMap;
}
/**
* @return the idClassMap
*/
public Map<Object, Class<? extends Field>> getIdClassMap() {
return idClassMap;
}
/**
* @param idClassMap the idClassMap to set
*/
public void setIdClassMap(Map<Object, Class<? extends Field>> idClassMap) {
this.idClassMap = idClassMap;
}
/**
* @return the classBuilderMap
*/
public Map<Class<?>, FieldBuilder> getClassBuilderMap() {
return classBuilderMap;
}
/**
* @param classBuilderMap the classBuilderMap to set
*/
public void setClassBuilderMap(Map<Class<?>, FieldBuilder> classBuilderMap) {
this.classBuilderMap.clear();
this.classBuilderMap.putAll(classBuilderMap);
}
}