/* * 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.configuration; import static com.alibaba.citrus.service.form.FormConstant.*; import static com.alibaba.citrus.util.ArrayUtil.*; import static com.alibaba.citrus.util.Assert.ExceptionType.*; import static com.alibaba.citrus.util.Assert.*; import static com.alibaba.citrus.util.CollectionUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import static java.util.Collections.*; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import com.alibaba.citrus.service.configuration.support.PropertyEditorRegistrarsSupport; import com.alibaba.citrus.service.form.FormService; import com.alibaba.citrus.service.form.configuration.FormConfig; import com.alibaba.citrus.service.form.configuration.GroupConfig; import com.alibaba.citrus.service.form.configuration.GroupConfig.Import; import org.springframework.beans.PropertyEditorRegistrar; /** * 实现<code>FormConfig</code>。 * * @author Michael Zhou */ public class FormConfigImpl extends AbstractConfig<FormConfig> implements FormConfig { private FormService formService; private FormService[] importFormServices; private Map<String, GroupConfigImpl> groups; // group name to groupConfig private Map<String, GroupConfigImpl> groupsByKey; // group key to groupConfig private List<GroupConfig> groupList; // unmodifiable group list private PropertyEditorRegistrarsSupport propertyEditorRegistrars = new PropertyEditorRegistrarsSupport(); private Boolean converterQuiet; private Boolean postOnlyByDefault; private FieldKeyFormat fieldKeyFormat; private String messageCodePrefix; /** 取得创建此form的service。 */ public FormService getFormService() { return formService; } /** 设置创建此form的service。 */ public void setFormService(FormService formService) { this.formService = assertNotNull(formService, "formService"); } /** 设置要导入的form。 */ public void setImports(FormService[] importFromServices) { this.importFormServices = importFromServices; } /** 类型转换出错时,是否不报错,而是返回默认值。 */ public boolean isConverterQuiet() { return converterQuiet == null ? true : converterQuiet.booleanValue(); } /** 设置类型转换出错时,是否不报错,而是返回默认值。 */ public void setConverterQuiet(boolean converterQuiet) { this.converterQuiet = converterQuiet; } /** Group是否默认必须从post请求中取得数据。 */ public boolean isPostOnlyByDefault() { return postOnlyByDefault == null ? true : postOnlyByDefault.booleanValue(); } /** 设置group是否默认必须从post请求中取得数据。 */ public void setPostOnlyByDefault(boolean postOnlyByDefault) { this.postOnlyByDefault = postOnlyByDefault; } /** 取得field key的格式,可以是压缩或不压缩的。 */ public FieldKeyFormat getFieldKeyFormat() { return fieldKeyFormat == null ? FieldKeyFormat.compressed : fieldKeyFormat; } /** 设置field key的格式,可以是压缩或不压缩的。 */ public void setFieldKeyFormat(FieldKeyFormat fieldKeyFormat) { this.fieldKeyFormat = fieldKeyFormat; } /** * 取得message code的前缀。 * <p> * Validator可以从spring <code>MessageSource</code> * 中取得message内容。用来引用message的code为: * <code>messageCodePrefix.groupName.fieldName.validatorId</code>。 * </p> * <p> * 默认的前缀为:<code>form.</code>。 * </p> */ public String getMessageCodePrefix() { return messageCodePrefix == null ? FORM_MESSAGE_CODE_PREFIX : messageCodePrefix; } /** 设置message code的前缀。 */ public void setMessageCodePrefix(String messageCodePrefix) { this.messageCodePrefix = normalizeMessageCodePrefix(messageCodePrefix); } private String normalizeMessageCodePrefix(String messageCodePrefix) { messageCodePrefix = trimToNull(messageCodePrefix); if (messageCodePrefix != null && !messageCodePrefix.endsWith(".")) { messageCodePrefix += "."; } return messageCodePrefix; } /** 取得所有group config的列表。 */ public List<GroupConfig> getGroupConfigList() { if (groupList == null) { return emptyList(); } else { return groupList; } } /** 取得指定名称的group config。名称大小写不敏感。 如果未找到,则返回<code>null</code>。 */ public GroupConfig getGroupConfig(String groupName) { if (groups == null) { return null; } else { return groups.get(caseInsensitiveName(groupName)); } } /** 取得和指定key相对应的group config。如果未找到,则返回<code>null</code> */ public GroupConfig getGroupConfigByKey(String groupKey) { return assertNotNull(groupsByKey, ILLEGAL_STATE, "groupsByKey not inited").get(groupKey); } /** 设置group configs。 */ public void setGroupConfigImplList(List<GroupConfigImpl> groupConfigList) { groups = null; addGroupConfigImplList(groupConfigList, false); } private void addGroupConfigImplList(List<GroupConfigImpl> groupConfigList, boolean importing) { if (groupConfigList != null) { if (groups == null) { groups = createLinkedHashMap(); } for (GroupConfigImpl groupConfig : groupConfigList) { String groupName = caseInsensitiveName(groupConfig.getName()); // 大小写不敏感! // 如果是importing form,允许重名,并忽略被import form中的group // 对于当前form中的group,不允许重名。 if (!importing) { assertTrue(!groups.containsKey(groupName), "Duplicated group name: %s", groupConfig.getName()); } if (!groups.containsKey(groupName)) { groups.put(groupName, groupConfig); } } } } /** * 取得<code>PropertyEditor</code>注册器。 * <p> * <code>PropertyEditor</code>负责将字符串值转换成bean property的类型,或反之。 * </p> */ public PropertyEditorRegistrar getPropertyEditorRegistrar() { return propertyEditorRegistrars; } /** * 设置一组<code>PropertyEditor</code>注册器。 * <p> * <code>PropertyEditor</code>负责将字符串值转换成bean property的类型,或反之。 * </p> */ public void setPropertyEditorRegistrars(PropertyEditorRegistrar[] registrars) { propertyEditorRegistrars.setPropertyEditorRegistrars(registrars); } /** 初始化form config。 */ @Override protected void init() throws Exception { // 处理form import。 if (!isEmptyArray(importFormServices)) { List<GroupConfigImpl> importGroups = createLinkedList(); for (FormService importFormService : importFormServices) { FormConfig importFormConfig = importFormService.getFormConfig(); for (GroupConfig importGroup : importFormConfig.getGroupConfigList()) { GroupConfigImpl srcGroupConfig = (GroupConfigImpl) importGroup; // expected same implementations GroupConfigImpl newGroupConfig = new GroupConfigImpl(); String groupName = srcGroupConfig.getName(); newGroupConfig.setName(groupName); newGroupConfig.extendsFrom(srcGroupConfig); importGroups.add(newGroupConfig); } } addGroupConfigImplList(importGroups, true); } // 初步初始化所有groups assertNotNull(groups, "no groups"); groupsByKey = createHashMap(); groupList = createArrayList(groups.size()); for (Map.Entry<String, GroupConfigImpl> entry : groups.entrySet()) { String caseInsensitiveName = entry.getKey(); GroupConfigImpl groupConfig = entry.getValue(); switch (getFieldKeyFormat()) { case uncompressed: groupConfig.setKey(caseInsensitiveName); break; default: // 设置不重复的、压缩的key for (int i = 1; i <= caseInsensitiveName.length(); i++) { String key = caseInsensitiveName.substring(0, i); if (!groupsByKey.containsKey(key)) { groupConfig.setKey(key); groupsByKey.put(key, groupConfig); break; } } break; } // 设置不压缩的key - 不可能重复,因为前面已经判断过了 groupsByKey.put(caseInsensitiveName, groupConfig); // 设置group.form groupConfig.setFormConfig(this); // 设置groupList groupList.add(groupConfig); } groupList = unmodifiableList(groupList); // 处理group之间的继承关系,将parent group和imports中的内容展开到子group中。 // 每个group.init2()将被调用。 Set<GroupConfigImpl> processedGroups = createHashSet(); GroupStack processingGroups = new GroupStack(); for (GroupConfig groupConfig : getGroupConfigList()) { processGroup((GroupConfigImpl) groupConfig, processedGroups, processingGroups); } } /** 处理group之间的继承关系,将parent group和imports中的内容展开到子group中。 */ private void processGroup(GroupConfigImpl groupConfig, Set<GroupConfigImpl> processedGroups, GroupStack processingGroups) throws Exception { if (!processedGroups.contains(groupConfig)) { if (groupConfig.getParentGroup() != null || !groupConfig.getImports().isEmpty()) { // 防止循环继承或import if (processingGroups.contains(groupConfig)) { StringBuilder buf = new StringBuilder(); for (GroupConfigImpl group : processingGroups) { if (buf.length() == 0) { buf.append("Cycle detected: "); } else { buf.append(" -> "); } buf.append(group.getName()); } buf.append(" -> ").append(groupConfig.getName()); throw new IllegalArgumentException(buf.toString()); } processingGroups.push(groupConfig); // 处理parentGroup if (groupConfig.getParentGroup() != null) { copyFields(groupConfig, groupConfig.getParentGroup(), null, true, processedGroups, processingGroups); } // 处理imports for (Import impot : groupConfig.getImports()) { copyFields(groupConfig, impot.getGroupName(), impot.getFieldName(), false, processedGroups, processingGroups); } processingGroups.pop(); } processedGroups.add(groupConfig); // 防止重复处理 groupConfig.init2(); // 初始化group } } private void copyFields(GroupConfigImpl targetGroup, String srcGroupName, String srcFieldName, boolean isExtends, Set<GroupConfigImpl> processedGroups, GroupStack processingGroups) throws Exception { GroupConfigImpl srcGroup = (GroupConfigImpl) assertNotNull(getGroupConfig(srcGroupName), "Parent or imported group name \"%s\" not found", srcGroupName); // 递归处理parentGroup和imported Groups。 processGroup(srcGroup, processedGroups, processingGroups); // 将parentGroup或imported Groups中的所有内容复制到当前group中。 if (isExtends) { targetGroup.extendsFrom(srcGroup); } else { targetGroup.importsFrom(srcGroup, srcFieldName); } } /** 转换成易于阅读的字符串。 */ @Override public String toString() { return "FormConfig[groups: " + getGroupConfigList().size() + "]"; } /** 用来防止group递归继承。 */ private static class GroupStack implements Iterable<GroupConfigImpl> { private final LinkedList<GroupConfigImpl> groups = createLinkedList(); public void push(GroupConfigImpl group) { groups.addLast(group); } public GroupConfigImpl pop() { return groups.removeLast(); } public boolean contains(GroupConfigImpl group) { return groups.contains(group); } public Iterator<GroupConfigImpl> iterator() { return groups.iterator(); } @Override public String toString() { return groups.toString(); } } }