/* 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.riotfamily.forms;
import java.beans.PropertyEditor;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.riotfamily.common.beans.property.SqlDateEditor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistrySupport;
import org.springframework.beans.propertyeditors.CustomDateEditor;
/**
* This class is used to bind a form element to the property of a bean. The
* form element must implement the {@link org.riotfamily.forms.Editor}
* interface.
*/
public abstract class AbstractEditorBinder extends PropertyEditorRegistrySupport
implements EditorBinder {
private Logger log = LoggerFactory.getLogger(AbstractEditorBinder.class);
/** List of {@link EditorBinding editor bindings} */
private Map<String, EditorBinding> bindings = new LinkedHashMap<String, EditorBinding>();
public AbstractEditorBinder() {
registerDefaultEditors();
registerCustomEditor(java.sql.Date.class,new SqlDateEditor());
registerCustomEditor(Date.class, new CustomDateEditor(
new SimpleDateFormat("yyyy-MM-dd"), false));
}
@Override
public abstract Class<?> getPropertyType(String path);
public Map<String, EditorBinding> getBindings() {
return this.bindings;
}
public EditorBinder replace(EditorBinder previousBinder) {
if (previousBinder != null) {
this.bindings = previousBinder.getBindings();
}
return this;
}
public void bind(Editor editor, String property) {
log.debug("Binding " + editor + " to property " + property);
if (bindings.containsKey(property)) {
EditorBindingImpl eb = (EditorBindingImpl) bindings.get(property);
eb.setEditor(editor);
editor.setEditorBinding(eb);
}
else {
EditorBinding eb = new EditorBindingImpl(editor, property);
bindings.put(property, eb);
editor.setEditorBinding(eb);
}
}
public Editor getEditor(String property) {
if (property != null) {
int i = property.indexOf('.');
if (i != -1) {
String nested = property.substring(i + 1);
try {
Editor editor = findEditorByProperty(property.substring(0, i));
if (editor instanceof NestedEditor) {
NestedEditor ne = (NestedEditor) editor;
return ne.getEditor(nested);
}
else {
throw new IllegalStateException("Editor for " + property
+ " must implement the NestedEditor interface");
}
}
catch (InvalidPropertyException e) {
//nested editor was not defined, fall back to direct binding
}
}
return findEditorByProperty(property);
}
return null;
}
protected Editor findEditorByProperty(String property) {
EditorBinding binding = bindings.get(property);
if (binding != null) {
return binding.getEditor();
}
throw new InvalidPropertyException(getBeanClass(), property,
"No editor bound to property");
}
public String[] getBoundProperties() {
String[] props = new String[bindings.size()];
Iterator<String> it = bindings.keySet().iterator();
for (int i = 0; it.hasNext(); i++) {
props[i] = it.next();
}
return props;
}
public void registerPropertyEditors(
Collection<PropertyEditorRegistrar> registrars) {
if (registrars != null) {
for (PropertyEditorRegistrar registrar : registrars) {
registrar.registerCustomEditors(this);
}
}
}
public void initEditors() {
for (EditorBinding binding : bindings.values()) {
Editor editor = binding.getEditor();
Object value = getPropertyValue(binding.getProperty());
editor.setValue(value);
}
}
public Object populateBackingObject() {
for (EditorBinding binding : bindings.values()) {
Editor editor = binding.getEditor();
if (editor.isEnabled()) {
Object value = editor.getValue();
setPropertyValue(binding.getProperty(), value);
}
}
return getBackingObject();
}
public PropertyEditor getPropertyEditor(Class<?> type, String propertyPath) {
PropertyEditor pe = findCustomEditor(type, propertyPath);
if (pe == null) {
pe = getDefaultEditor(type);
}
return pe;
}
protected String getPropertyPath(Editor editor, String property) {
EditorBinding parentBinding = findParentBinding(editor);
if (parentBinding != null) {
return parentBinding.getPropertyPath() + '.' + property;
}
return property;
}
private EditorBinding findParentBinding(Editor editor) {
Element parent = editor.getParent();
while (parent != null) {
if (parent instanceof Editor) {
Editor parentEditor = (Editor) parent;
if (parentEditor.getEditorBinding() != null) {
return parentEditor.getEditorBinding();
}
}
parent = parent.getParent();
}
return null;
}
private class EditorBindingImpl implements EditorBinding {
private Editor editor;
private String property;
public EditorBindingImpl(Editor editor, String property) {
this.editor = editor;
this.property = property;
}
public EditorBinder getEditorBinder() {
return AbstractEditorBinder.this;
}
public void setEditor(Editor editor) {
this.editor = editor;
}
public Editor getEditor() {
return editor;
}
public String getProperty() {
return property;
}
public Object getValue() {
return getPropertyValue(property);
}
public Class<?> getBeanClass() {
return AbstractEditorBinder.this.getBeanClass();
}
public boolean isEditingExistingBean() {
return AbstractEditorBinder.this.isEditingExistingBean();
}
public String getPropertyPath() {
return AbstractEditorBinder.this.getPropertyPath(editor, property);
}
public Class<?> getPropertyType() {
return AbstractEditorBinder.this.getPropertyType(property);
}
public PropertyEditor getPropertyEditor() {
return AbstractEditorBinder.this.getPropertyEditor(
getPropertyType(), getPropertyPath());
}
}
}