/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* 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.alibaba.citrus.service.form.impl;
import static com.alibaba.citrus.service.form.FormConstant.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import com.alibaba.citrus.service.form.Field;
import com.alibaba.citrus.service.form.Form;
import com.alibaba.citrus.service.form.Group;
import com.alibaba.citrus.service.form.InvalidGroupStateException;
import com.alibaba.citrus.service.form.MessageContext;
import com.alibaba.citrus.service.form.configuration.FieldConfig;
import com.alibaba.citrus.service.form.configuration.GroupConfig;
import com.alibaba.citrus.util.ObjectUtil;
import com.alibaba.citrus.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.CollectionFactory;
import org.springframework.core.MethodParameter;
/**
* 代表用户所提交表单中的一组字段。
* <p>
* 注意:group对象不是线程安全的,不能被多线程共享。
* </p>
*
* @author Michael Zhou
*/
public class GroupImpl implements Group {
protected static final Logger log = LoggerFactory.getLogger(Group.class);
private final GroupConfig groupConfig;
private final Form form;
private final String groupKey;
private final String instanceKey;
private final Map<String, Field> fields = createLinkedHashMap();
private final Collection<Field> fieldList = Collections.unmodifiableCollection(fields.values());
private final MessageContext messageContext;
private boolean validated;
private boolean valid;
/** 创建一个新group。 */
public GroupImpl(GroupConfig groupConfig, Form form, String instanceKey) {
this.groupConfig = groupConfig;
this.form = form;
this.instanceKey = instanceKey;
this.groupKey = form.getKey() + FIELD_KEY_SEPARATOR + groupConfig.getKey() + FIELD_KEY_SEPARATOR + instanceKey;
this.messageContext = MessageContextFactory.newInstance(this);
}
/** 取得group的配置信息。 */
public GroupConfig getGroupConfig() {
return groupConfig;
}
/** 取得包含此group的form。 */
public Form getForm() {
return form;
}
/** 取得group name,相当于<code>getGroupConfig().getName()</code> */
public String getName() {
return getGroupConfig().getName();
}
/**
* 取得代表group的key。
* <p>
* 由固定前缀<code>"_fm"</code>,加上group名的缩写,再加上group instance key构成。例如:
* <code>_fm.m._0</code>。
* </p>
*/
public String getKey() {
return groupKey;
}
/** 取得标识当前group的instance key。 */
public String getInstanceKey() {
return instanceKey;
}
/** 判定group是否通过验证。 */
public boolean isValid() {
return valid;
}
/**
* 设置group的是否通过验证。
* <p>
* 注意:该值将被叠加到当前的状态中:<code>this.valid &= valid</code>
* </p>
*/
protected void setValid(boolean valid) {
this.valid &= valid;
((FormImpl) getForm()).setValid(this.valid);
}
/**
* 判定该group是否被置值,并验证。在两种情况下,<code>isValidated()</code>为<code>true</code>。
* <ol>
* <li>用户提交包含当前group字段的表单。此时相应的group被初始化并验证。</li>
* <li>程序调用<code>validate()</code>方法。这种方式下,group中的字段值可以由程序来设置,效果如同用户提交表单一样。</li>
* </ol>
*/
public boolean isValidated() {
return validated;
}
/** 初始化group。 */
public void init() {
init(null);
}
/**
* 初始化group。 其中, <code>request</code>可以是<code>null</code>,如果
* <code>request</code>不为<code>null</code>,则同时验证表单。
*/
void init(FormParameters request) {
fields.clear();
valid = true;
validated = request != null;
for (FieldConfig fieldConfig : getGroupConfig().getFieldConfigList()) {
FieldImpl field = new FieldImpl(fieldConfig, this);
fields.put(StringUtil.toLowerCase(fieldConfig.getName()), field);
field.init(request);
}
// 集中验证表单字段(有些validator需要读取多个字段的值,这样做是为了避免这些validator读不到在其后定义的field的值)
if (request != null) {
for (Field field : fields.values()) {
((FieldImpl) field).validate();
}
}
}
/**
* 验证(或重新验证)当前的字段值。
* <p>
* 注意,此方法将设置<code>isValidated()</code>为<code>true</code>。
* </p>
*/
public void validate() {
valid = true;
validated = true;
for (Field field : getFields()) {
((FieldImpl) field).validate();
}
}
/** 取得所有fields的列表。 */
public Collection<Field> getFields() {
return fieldList;
}
/** 取得指定名称的field。field名称(大小写不敏感) */
public Field getField(String fieldName) {
return fields.get(StringUtil.toLowerCase(fieldName));
}
/** 取得group级别的错误信息表达式的context。 */
protected MessageContext getMessageContext() {
return messageContext;
}
/**
* 将对象中的属性值置入到fields中。
* <p>
* 对于<code>isValidated()</code>为<code>true</code>的group,该方法无效。
* </p>
*/
public void mapTo(Object object) {
if (isValidated() || object == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("Mapping properties to fields: group=\"{}\", object={}", getName(),
ObjectUtil.identityToString(object));
}
BeanWrapper bean = new BeanWrapperImpl(object);
getForm().getFormConfig().getPropertyEditorRegistrar().registerCustomEditors(bean);
for (Field field : getFields()) {
String propertyName = field.getFieldConfig().getPropertyName();
if (bean.isReadableProperty(propertyName)) {
Object propertyValue = bean.getPropertyValue(propertyName);
Class<?> propertyType = bean.getPropertyType(propertyName);
PropertyEditor editor = bean.findCustomEditor(propertyType, propertyName);
if (editor == null) {
editor = BeanUtils.findEditorByConvention(propertyType);
}
if (editor == null) {
if (propertyType.isArray() || CollectionFactory.isApproximableCollectionType(propertyType)) {
field.setValues((String[]) bean.convertIfNecessary(propertyValue, String[].class));
} else {
field.setValue(bean.convertIfNecessary(propertyValue, String.class));
}
} else {
editor.setValue(propertyValue);
field.setValue(editor.getAsText());
}
} else {
log.debug("No readable property \"{}\" found in type {}", propertyName, object.getClass().getName());
}
}
}
/**
* 将group中的值置入指定对象。
* <p>
* 对于<code>isValidated()</code>为<code>false</code>的group,该方法无效。
* </p>
*/
public void setProperties(Object object) {
if (!isValidated() || object == null) {
return;
}
if (isValid()) {
if (log.isDebugEnabled()) {
log.debug("Set validated properties of group \"" + getName() + "\" to object "
+ ObjectUtil.identityToString(object));
}
BeanWrapper bean = new BeanWrapperImpl(object);
getForm().getFormConfig().getPropertyEditorRegistrar().registerCustomEditors(bean);
for (Field field : getFields()) {
String propertyName = field.getFieldConfig().getPropertyName();
if (bean.isWritableProperty(propertyName)) {
PropertyDescriptor pd = bean.getPropertyDescriptor(propertyName);
MethodParameter mp = BeanUtils.getWriteMethodParameter(pd);
Object value = field.getValueOfType(pd.getPropertyType(), mp, null);
bean.setPropertyValue(propertyName, value);
} else {
log.debug("No writable property \"{}\" found in type {}", propertyName, object.getClass().getName());
}
}
} else {
throw new InvalidGroupStateException("Attempted to call setProperties from an invalid input");
}
}
/** 转换成易于阅读的字符串。 */
@Override
public String toString() {
return "Group[name: " + getName() + "." + getInstanceKey() + ", fields: "
+ getGroupConfig().getFieldConfigList().size() + ", validated: " + isValidated() + ", valid: "
+ isValid() + "]";
}
}