Package com.alibaba.citrus.service.form.impl

Source Code of com.alibaba.citrus.service.form.impl.FieldImpl

/*
* 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.Assert.*;
import static com.alibaba.citrus.util.BasicConstant.*;
import static com.alibaba.citrus.util.ObjectUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Map;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;

import com.alibaba.citrus.service.form.CustomErrorNotFoundException;
import com.alibaba.citrus.service.form.Field;
import com.alibaba.citrus.service.form.Group;
import com.alibaba.citrus.service.form.MessageContext;
import com.alibaba.citrus.service.form.Validator;
import com.alibaba.citrus.service.form.Validator.Context;
import com.alibaba.citrus.service.form.configuration.FieldConfig;
import com.alibaba.citrus.service.requestcontext.support.ValueListSupport;
import com.alibaba.citrus.util.ArrayUtil;
import com.alibaba.citrus.util.ObjectUtil;
import com.alibaba.citrus.util.StringEscapeUtil;
import com.alibaba.citrus.util.io.ByteArrayInputStream;
import com.alibaba.citrus.util.io.ByteArrayOutputStream;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* 代表用户所提交表单中的一个field。
* <p>
* 注意:field对象不是线程安全的,不能被多线程共享。
* </p>
*
* @author Michael Zhou
*/
public class FieldImpl extends ValueListSupport implements Field {
    private final static Logger log = LoggerFactory.getLogger(Field.class);
    private final FieldConfig    fieldConfig;
    private final Group          group;
    private final String         fieldKey;
    private final MessageContext messageContext;
    private       boolean        valid;
    private       String         message;
    private       Attachment     attachment;

    /** 创建一个新field。 */
    public FieldImpl(FieldConfig fieldConfig, Group group) {
        super( //
               assertNotNull(group, "group").getForm().getTypeConverter(), // converter
               fieldConfig.getGroupConfig().getFormConfig().isConverterQuiet() // converter quiet
        );
        this.fieldConfig = assertNotNull(fieldConfig, "fieldConfig");
        this.group = group;
        this.fieldKey = group.getKey() + FIELD_KEY_SEPARATOR + fieldConfig.getKey();
        this.messageContext = MessageContextFactory.newInstance(this);
    }

    /** 取得field的配置信息。 */
    public FieldConfig getFieldConfig() {
        return fieldConfig;
    }

    /** 取得包含此field的group。 */
    public Group getGroup() {
        return group;
    }

    /** 判定field是否通过验证。 */
    public boolean isValid() {
        return valid;
    }

    /**
     * 取得在form中唯一代表该field的key。
     * <p>
     * 由固定前缀<code>"_fm"</code>,加上group名的缩写,加上group instance
     * fieldKey,再加上field名的缩写构成。例如:<code>_fm.m._0.n</code>
     * </p>
     */
    public String getKey() {
        return fieldKey;
    }

    /**
     * 取得在form中唯一代表该field的key,当用户提交的表单中未包含此field的信息时,取这个key的值作为该field的值。
     * <p>
     * 这对于checkbox之类的HTML控件特别有用。
     * </p>
     * <p>
     * Key的格式为:<code>_fm.groupKey.instanceKey.fieldKey.absent</code>
     * </p>
     */
    public String getAbsentKey() {
        return getKey() + FORM_FIELD_ABSENT_KEY;
    }

    /**
     * 取得在form中和当前field绑定的附件的key。
     * <p>
     * Key的格式为:<code>_fm.groupKey.instanceKey.fieldKey.attach</code>
     * </p>
     */
    public String getAttachmentKey() {
        return getKey() + FORM_FIELD_ATTACHMENT_KEY;
    }

    /** 取得出错信息。 */
    public String getMessage() {
        return message;
    }

    /**
     * 设置错误信息,同时置<code>isValid()</code><code>false</code>
     * <p>
     * 对于<code>isValid()</code>已经是<code>false</code>的字段,该方法无效(不覆盖现有的错误信息)
     * </p>
     * <p>
     * id表示错误信息的ID,必须定义的form描述文件中。
     * </p>
     */
    public void setMessage(String id) {
        setMessage(id, null);
    }

    /**
     * 设置错误信息,同时置<code>isValid()</code><code>false</code>
     * <p>
     * 对于<code>isValid()</code>已经是<code>false</code>的字段,该方法无效(不覆盖现有的错误信息)
     * </p>
     * <p>
     * id表示错误信息的ID,必须定义的form描述文件中。params表示生成错误信息的参数表。
     * </p>
     */
    public void setMessage(String id, Map<String, ?> params) {
        if (isValid()) {
            boolean found = false;

            for (Validator validator : getFieldConfig().getValidators()) {
                if (isEquals(validator.getId(), id)) {
                    MessageContext expressionContext = MessageContextFactory.newInstance(this, validator);
                    expressionContext.putAll(params);
                    valid = false;
                    found = true;
                    message = validator.getMessage(new ValidatorContextImpl(expressionContext, this));

                    if (message == null) {
                        throw new CustomErrorNotFoundException("No message specified for error ID \"" + id + "\" in "
                                                               + this);
                    }

                    break;
                }
            }

            if (found) {
                ((GroupImpl) getGroup()).setValid(valid);
            } else {
                throw new CustomErrorNotFoundException("Specified error ID \"" + id + "\" was not found in " + this);
            }
        }
    }

    /** 初始化field值,但不验证表单字段。其中,<code>request</code>可以是<code>null</code>。 */
    public void init(FormParameters request) {
        valid = true;
        attachment = null;

        // request为null,表示是空表单(不是用户提交的),此时装载默认值
        if (request == null) {
            setValues(getFieldConfig().getDefaultValues());
        } else {
            setValues(request.getValues(getKey()));

            // 如果field不存在,则检查absent fieldKey。
            if (size() == 0) {
                setValues(request.getValues(getAbsentKey()));
            }

            // 如果存在attachment,则装入之
            String attachmentEncoded = trimToNull(request.getStringValue(getAttachmentKey()));

            if (attachmentEncoded != null) {
                attachment = new Attachment(attachmentEncoded);
            }
        }
    }

    /** 验证(或重新验证)字段。 */
    protected void validate() {
        valid = true;

        for (Validator validator : getFieldConfig().getValidators()) {
            MessageContext expressionContext = MessageContextFactory.newInstance(this, validator);
            Context context = new ValidatorContextImpl(expressionContext, this);
            boolean passed = validator.validate(context);

            if (!passed) {
                valid = false;
                message = validator.getMessage(context);
                break;
            }
        }

        ((GroupImpl) getGroup()).setValid(valid);
    }

    /** 取得field级别的错误信息表达式的context。 */
    protected MessageContext getMessageContext() {
        return messageContext;
    }

    /** 取得field name,相当于<code>getFieldConfig().getName()</code>。 */
    public String getName() {
        return getFieldConfig().getName();
    }

    /** 取得参数值,如果指定名称的参数不存在,则返回<code>""</code>。 */
    @Override
    public String getStringValue() {
        return getStringValue(EMPTY_STRING);
    }

    /** 取得用来显示field的名称,相当于<code>getFieldConfig().getDisplayName()</code>。 */
    public String getDisplayName() {
        return getFieldConfig().getDisplayName();
    }

    /** 取得默认值,相当于<code>getFieldConfig().getDefaultValue()</code>。 */
    public String getDefaultValue() {
        return getFieldConfig().getDefaultValue();
    }

    /** 取得默认值,相当于<code>getFieldConfig().getDefaultValues()</code>。 */
    public String[] getDefaultValues() {
        return getFieldConfig().getDefaultValues();
    }

    /** 添加参数名/参数值。 */
    @Override
    public void addValue(Object value) {
        if (getFieldConfig().isTrimming() && value instanceof String) {
            value = trimToNull((String) value);
        }

        super.addValue(value);
    }

    /** 设置附件。 */
    public Object getAttachment() {
        return attachment == null ? null : attachment.getAttachment();
    }

    /** 设置编码后的附件。 */
    public String getAttachmentEncoded() {
        return attachment == null ? null : attachment.getAttachmentEncoded();
    }

    /** 是否包含附件? */
    public boolean hasAttachment() {
        return attachment != null && attachment.getAttachment() != null;
    }

    /**
     * 设置附件。
     * <p>
     * 注意,当attachment已经存在时,该方法调用无效。欲强制设入,请先调用<code>clearAttachment()</code>
     * </p>
     */
    public void setAttachment(Object attachment) {
        if (this.attachment == null) {
            this.attachment = new Attachment(attachment);
        }
    }

    /** 清除附件。 */
    public void clearAttachment() {
        this.attachment = null;
    }

    /** 转换成易于阅读的字符串。 */
    @Override
    public String toString() {
        return "Field[group: " + getGroup().getGroupConfig().getName() + "." + getGroup().getInstanceKey() + ", name: "
               + getFieldConfig().getName() + ", values: " + ObjectUtil.toString(getValues()) + ", valid: "
               + isValid() + "]";
    }

    /** 代表一个附件。 */
    private static class Attachment {
        private Object attachment;
        private String attachmentEncoded;

        public Attachment(Object attachment) {
            setAttachment(attachment);
        }

        public Attachment(String attachmentEncoded) {
            setAttachment(decode(attachmentEncoded));
        }

        public Object getAttachment() {
            return attachment;
        }

        public void setAttachment(Object attachment) {
            this.attachment = attachment;
            this.attachmentEncoded = null;
        }

        public String getAttachmentEncoded() {
            if (attachment != null && attachmentEncoded == null) {
                attachmentEncoded = encode(attachment);
            }

            return attachmentEncoded;
        }

        private String encode(Object attachment) {
            if (attachment == null) {
                return null;
            }

            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();

                // 1. 序列化
                // 2. 压缩
                Deflater def = new Deflater(Deflater.BEST_COMPRESSION, false);
                DeflaterOutputStream dos = new DeflaterOutputStream(baos, def);
                ObjectOutputStream oos = null;

                try {
                    oos = new ObjectOutputStream(dos);
                    oos.writeObject(attachment);
                } finally {
                    if (oos != null) {
                        try {
                            oos.close();
                        } catch (IOException e) {
                        }
                    }

                    def.end();
                }

                byte[] plaintext = baos.toByteArray().toByteArray();

                // 3. base64编码
                return StringEscapeUtil.escapeURL(new String(Base64.encodeBase64(plaintext, false), "ISO-8859-1"));
            } catch (Exception e) {
                log.error("Failed to encode field attachment", e);
                return "!Failure: " + e;
            }
        }

        private Object decode(String attachmentEncoded) {
            if (attachmentEncoded == null || attachmentEncoded.startsWith("!Failure:")) {
                return null;
            }

            // 1. base64解码
            byte[] plaintext = null;

            try {
                String encoded = StringEscapeUtil.unescapeURL(attachmentEncoded);
                plaintext = Base64.decodeBase64(encoded.getBytes("ISO-8859-1"));

                if (ArrayUtil.isEmptyArray(plaintext)) {
                    log.warn("Field attachment content is empty: " + encoded);
                    return null;
                }
            } catch (Exception e) {
                log.warn("Failed to decode field attachment: " + e);
                return null;
            }

            // 2. 解压缩
            ByteArrayInputStream bais = new ByteArrayInputStream(plaintext);
            Inflater inf = new Inflater(false);
            InflaterInputStream iis = new InflaterInputStream(bais, inf);

            // 3. 反序列化
            ObjectInputStream ois = null;

            try {
                ois = new ObjectInputStream(iis);
                return ois.readObject();
            } catch (Exception e) {
                log.warn("Failed to parse field attachment", e);
            } finally {
                if (ois != null) {
                    try {
                        ois.close();
                    } catch (IOException e) {
                    }
                }

                inf.end();
            }

            return null;
        }
    }
}
TOP

Related Classes of com.alibaba.citrus.service.form.impl.FieldImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.