com.surveypanel.form.Form.java Source code

Java tutorial

Introduction

Here is the source code for com.surveypanel.form.Form.java

Source

/*
    
* SurveyPanel
* Copyright (C) 2009 Serge Tan Panza
* All rights reserved.
* License: GNU/GPL License v3 , see LICENSE.txt
* SurveyPanel is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.txt for copyright notices and details.
* 
*/
package com.surveypanel.form;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;

import com.surveypanel.form.validation.ValidationMapConfig;
import com.surveypanel.form.validation.ValidationResult;
import com.surveypanel.form.validation.Validator;
import com.surveypanel.view.FreemarkerManager;
import com.surveypanel.view.FreemarkerTemplate;

import freemarker.cache.TemplateLoader;

/**
 * @author stanpanza
 *
 */
public class Form extends ScriptableObject {

    private static final long serialVersionUID = 7270302137332842441L;
    protected Log log = LogFactory.getLog(Form.class);
    private Map<String, Object> frmValues = new HashMap<String, Object>();
    private Map<String, Object> variables = new HashMap<String, Object>();
    private Map<String, ValidationResult> validations = new LinkedHashMap<String, ValidationResult>();
    private Questionnaire questionnaire;
    private PrintWriter writer;
    private String id;
    private boolean backEnable;
    private boolean debug = false;
    private boolean devMode = false;
    private boolean qualified, finish;
    private Date created, updated, ended;
    private FormFactory formFactory;
    private List<Map<String, Object>> currentQuestions = new ArrayList<Map<String, Object>>();
    private Locale locale = new Locale("en", "GB", "");
    private Map<String, String> renderedKeys = new TreeMap<String, String>();

    public Form(FormDTO formDTO, Questionnaire qst) {
        frmValues = qst.getDefaultAnswerMap();
        questionnaire = qst;
        id = formDTO.getId();
        variables = formDTO.getVariables();
        copyValues(formDTO.getValues());
        finish = formDTO.isFinish();
        ended = formDTO.getEnded();
    }

    public Form(String id, Map<String, Object> values, Questionnaire questionnaire) {
        this.frmValues = questionnaire.getDefaultAnswerMap();
        this.questionnaire = questionnaire;
        copyValues(values);
        this.id = id;
    }

    private void copyValues(Map<String, Object> values) {
        for (String key : frmValues.keySet()) {
            Object value = values.get(key);
            if (value != null) {
                frmValues.put(key, value);
            }

        }
    }

    public Form(String id, Map<String, Object> values, Map<String, Object> variables, Questionnaire questionnaire) {
        this(id, values, questionnaire);
        this.variables = variables;
    }

    public boolean has(String fieldName) {
        return frmValues.containsKey(fieldName) ? true : variables.containsKey(fieldName);
    }

    public Object get(String fieldName, Scriptable start) {
        if (this.frmValues.containsKey(fieldName)) {
            return this.frmValues.get(fieldName);
        }
        return this.variables.get(fieldName);
    }

    public void put(String name, Scriptable start, Object value) {
        if (this.frmValues.containsKey(name)) {
            this.frmValues.put(name, value);
        } else {
            this.variables.put(name, value);
        }
    }

    public void remove(String fieldName) {
        this.variables.remove(fieldName);
    }

    public Object[] getFieldNames() {
        return this.frmValues.keySet().toArray();
    }

    public boolean hasAnswer(String fieldName) {
        return this.frmValues.get(fieldName) != null;
    }

    public void addError(String questionId, String errorMsg) {
        if (!validations.containsKey(questionId)) {
            validations.put(questionId, new ValidationResult(questionId));
        }
        ValidationResult validationResult = validations.get(questionId);
        validationResult.addError(errorMsg);
    }

    public void display(String templateLocation) {
        FreemarkerTemplate template = FreemarkerManager.getTemplate(this, templateLocation);
        try {
            template.render(writer);
            String[] questionNames = new String[currentQuestions.size()];
            for (int i = 0; i < currentQuestions.size(); i++) {
                questionNames[i] = (String) currentQuestions.get(i).get("name");
            }
            variables.put("questionNames", questionNames);
        } catch (Exception e) {
            log.error(e);
        }
    }

    public FormData getFormData(String questionId) {
        FormData formData = null;
        Map<String, Object> dataMap = questionnaire.getDataMap();
        QuestionImpl question = questionnaire.getQuestion(questionId);
        Object dataKey = dataMap.get(question.getName());
        if (dataKey instanceof String) {
            formData = new FormData((String) dataKey, frmValues.get(dataKey));
        } else if (dataKey.getClass().isArray()) {
            String[] keys = (String[]) dataKey;
            List<FormData> frmDatas = new ArrayList<FormData>();
            for (int i = 0; i < keys.length; i++) {
                String key = keys[i];
                String valueKey = question.getName() + "_" + key;
                Object value = frmValues.get(valueKey);
                if (value != null && !StringUtils.isEmpty(value.toString().trim())) {
                    FormData data = new FormData(key, value);
                    frmDatas.add(data);
                }
            }
            formData = new FormData(question.getName(), frmDatas.toArray(new FormData[] {}));
        }
        return formData;
    }

    public void validate() {
        Map<String, Object> dataMap = questionnaire.getDataMap();
        if (variables.containsKey("questionNames")) {
            String[] questionNames = (String[]) variables.get("questionNames");
            for (String questionId : questionNames) {
                QuestionImpl question = questionnaire.getQuestion(questionId);

                // internal validation
                FormData formData = null;
                Object dataKey = dataMap.get(question.getName());
                if (dataKey instanceof String) {
                    formData = new FormData((String) dataKey, frmValues.get(dataKey));
                } else if (dataKey.getClass().isArray()) {
                    String[] keys = (String[]) dataKey;
                    List<FormData> frmDatas = new ArrayList<FormData>();
                    FormData[] datas = new FormData[keys.length];
                    for (int i = 0; i < keys.length; i++) {
                        String key = keys[i];
                        String valueKey = question.getName() + "_" + key;
                        Object value = variables.get(valueKey);
                        FormData data = new FormData(key, value);
                        if (value != null) {
                            frmDatas.add(data);
                        }
                        datas[i] = data;
                    }

                    formData = new FormData(question.getName(), datas);
                }
                try {
                    ValidationResult vld = validate(question, formData);
                    if (vld.hasErrors()) {
                        validations.put(question.getName(), vld);
                    }
                } catch (Exception e) {
                    ValidationResult vld = new ValidationResult(question.getName());
                    vld.addError(question.getName(), getText("validation.error", new Object[] { e.getMessage() }));
                    validations.put(question.getName(), vld);
                }
            }
        }
    }

    /**
     * Process the given FormData object.  The FormData object will first be validated and then stored in the target
     * object by invoking the setXXX method for the given field.
     *
     * @param formData The FormData object
     * @return The ValidationResult
     * @throws Exception Any exception
     */
    protected ValidationResult process(QuestionImpl question, FormData formData, Locale locale) throws Exception {
        String name = question.getName();

        if (log.isDebugEnabled())
            log.debug("Validating field " + name);

        ValidationResult validationResult = validate(question, formData);
        validations.put(name, validationResult);
        log.debug("Validation complete.");

        // the current process is that only form data with a
        // defined form element will be written to the target
        // object...the other possible behavior is to process
        // store and convert for each FormData object -AE
        Object value = formData.getValue();
        try {
            if (value != null) {
                value = convert(question, value, locale);
            }
        } catch (NoSuchMethodException e) {
            log.info("No method found for parameter " + name);
        } catch (Exception e) {
            log.error("Error writing field " + name + ":" + e.getMessage());
        }

        return validationResult;
    }

    /**
     * Validate the given form data.
     *
     * @param formElement The FormElement
     * @param formData The form data
     * @return The ValidationResult
     * @throws Exception Any exception
     */
    protected ValidationResult validate(QuestionImpl question, FormData formData) throws Exception {
        ValidationResult result = new ValidationResult(question.getName());
        ValidationMapConfig validatorCfg = question.getValidationMapConfig();
        if (validatorCfg != null) {
            Set<String> types = validatorCfg.getTypes();
            for (String type : types) {
                Validator validator = questionnaire.getDefinition().getValidator(type);
                if (log.isDebugEnabled())
                    log.debug("Using validator:" + validator);
                validator.validate(question, formData, locale, validatorCfg.getProperties(type), result);
            }
        }
        boolean hasExclusive = false;
        int countAnswers = 0;
        Object value = formData.getValue();
        if (value.getClass().isArray()) {
            FormData[] values = (FormData[]) value;
            for (FormData data : values) {
                if (data.getName().endsWith("_open")) {
                    String answerName = data.getName().replace("_open", "");
                    if (hasBeenAnswered(answerName, values)) {
                        AnswerImpl answer = question.getAnswerByName(answerName);
                        int size = answer.getSize();
                        if (answer.isRequired() && StringUtils.isBlank((String) data.getValue())) {
                            result.addError(question.getName(), questionnaire.getText("form.open.field.required",
                                    new Object[] { question.getName() }, locale));
                        } else if (!StringUtils.isBlank((String) data.getValue())) {
                            String text = (String) data.getValue();
                            if (text.length() > size) {
                                result.addError(question.getName(), questionnaire.getText("form.open.field.max",
                                        new Object[] { question.getName() }, locale));
                            }
                        }
                    }
                } else {
                    if (data.getValue() != null) {
                        if (!hasExclusive) {
                            hasExclusive = question.isExclusive(data.getName());
                        }
                        countAnswers++;
                    }
                }

            }
        }

        if (hasExclusive && countAnswers > 1) {
            ValidationResult validationResult = null;
            if (validations.containsKey(question.getName())) {
                validationResult = validations.get(question.getName());
            } else {
                validationResult = new ValidationResult(question.getName());
            }
            validationResult.addError(question.getName(), "error.exclusive");
            validations.put(question.getName(), validationResult);
        }
        return result;
    }

    private boolean hasBeenAnswered(String answerName, FormData[] data) {
        for (FormData formData : data) {
            if (formData.getName().equals(answerName) && !StringUtils.isBlank((String) formData.getValue())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Convert the given value from one type to another using the converter assigned to the given FormElement.
     *
     * @param question The FormElement
     * @param value The value
     * @param locale The locale
     * @return The new typed object
     * @throws Exception Any exception
     */
    protected Object convert(QuestionImpl question, Object value, Locale locale) throws Exception {
        if (value == null) {
            return null;
        }

        TypeConverter typeConverter = new SimpleTypeConverter();

        if (typeConverter != null) {
            if (log.isDebugEnabled())
                log.debug("Converting " + value + " of type " + value.getClass() + " using "
                        + typeConverter.getClass());
            value = typeConverter.convertIfNecessary(value, null);
            if (log.isDebugEnabled()) {
                log.debug("New value: " + value);
                log.debug("New type: " + value.getClass());
            }
        }
        return value;
    }

    public Map<String, Object> getFieldValues() {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.putAll(frmValues);
        return map;
    }

    public void reset() {
        frmValues = questionnaire.getDefaultAnswerMap();
        ;
        variables.clear();
        validations.clear();
        id = null;
        currentQuestions.clear();
    };

    public String getId() {
        return id;
    }

    public long getSurveyId() {
        return questionnaire.getSurveyId();
    }

    public void setWriter(PrintWriter writer) {
        this.writer = writer;
    }

    public void setId(String id) {
        this.id = id;
    }

    /**
     * @return the backEnable
     */
    public boolean isBackEnable() {
        return backEnable;
    }

    public boolean save() {
        try {
            formFactory.update(this);
        } catch (Exception e) {
            log.error(e);
            return false;
        }
        return true;
    }

    public void resume() {
        Form form = formFactory.load(id, questionnaire.getSurveyId(), debug);
        this.frmValues = form.frmValues;
        this.variables = form.variables;
        this.questionnaire = form.questionnaire;
        this.id = form.id;
        this.backEnable = form.backEnable;
    }

    public String info() {
        return variables.toString() + frmValues.toString();
    }

    /**
     * @param formFactory the formFactory to set
     */
    public void setFormFactory(FormFactory formFactory) {
        this.formFactory = formFactory;
    }

    /**
     * @return the writer
     */
    public PrintWriter getWriter() {
        return writer;
    }

    /**
     * @return the variables
     */
    public Map<String, Object> getVariables() {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.putAll(variables);
        return map;
    }

    public QuestionImpl getQuestion(String questionId) {
        return questionnaire.getQuestion(questionId);
    }

    /**
     * @param question
     */
    public void addQuestion(Map<String, Object> question) {
        currentQuestions.add(question);
    }

    /**
     * @return the currentQuestions
     */
    public List<Map<String, Object>> getCurrentQuestions() {
        return currentQuestions;
    }

    /**
     * @return
     */
    public boolean hasError() {
        return !validations.isEmpty();
    }

    /**
     * @return
     */
    public boolean hasError(String fieldName) {
        return validations.containsKey(fieldName);
    }

    /**
     * @return
     */
    public Collection<ValidationResult> getErrors() {
        return validations.values();
    }

    /**
     * @return
     */
    public ValidationResult getError(String fieldName) {
        ValidationResult validationResult = validations.get(fieldName);
        return validationResult != null ? validationResult : new ValidationResult(fieldName);
    }

    /**
     * @param question
     * TODO only add keys in the questionnaire
     */
    public void setValues(Map<String, String> values) {
        for (Entry<String, String> element : values.entrySet()) {
            String key = element.getKey();
            String value = element.getValue();

            if (value.startsWith("col_")) {
                String[] ids = value.split(",");
                for (String id : ids) {
                    String[] fields = id.split("_");
                    if (fields.length == 3) {
                        value = fields[2];
                        if (value.trim().length() > 0) {
                            this.variables.put(key + "_" + fields[1], value);
                            this.variables.put(key, value);
                            this.frmValues.put(key, value);
                        }
                    }
                    if (fields.length == 5) {
                        value = fields[4];
                        if (value.trim().length() > 0) {
                            this.variables.put(key + "_" + fields[3], value);
                            this.variables.put(key, value);
                            this.frmValues.put(key, value);
                        }
                    }
                }
            } else {
                if (frmValues.containsKey(key)) {
                    if (value.trim().length() > 0) {
                        this.frmValues.put(key, value);
                        this.variables.put(key, value);
                    } else {
                        this.frmValues.put(key, null);
                        this.variables.put(key, null);
                    }
                }
            }
        }
    }

    /**
     * @return
     */
    public TemplateLoader getTemplateLoader() {
        return formFactory.getTemplateLoader(questionnaire.getSurveyId());
    }

    /**
     * @return the debug
     */
    public boolean isDebug() {
        return debug;
    }

    /**
     * @param debug the debug to set
     */
    public void setDebug(boolean debug) {
        this.debug = debug;
    }

    /**
     * @param key
     * @return
     */
    public String getText(String key) {
        return getText(key, new Object[] {});
    }

    /**
     * @param key
     * @param args
     * @return
     */
    public String getText(String key, Object[] args) {
        String text = questionnaire.getText(key, args, locale);
        if (devMode)
            renderedKeys.put(key, text);
        return text;
    }

    /**
     * @return the questionnaire
     */
    public Questionnaire getQuestionnaire() {
        return questionnaire;
    }

    /**
     * @return the locale
     */
    public Locale getLocale() {
        return locale;
    }

    /**
     * @param backEnable the backEnable to set
     */
    public void setBackEnable(boolean backEnable) {
        this.backEnable = backEnable;
    }

    /**
     * @param locale the locale to set
     */
    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    public FormDTO toDTO() throws Exception {
        FormDTO formDTO = new FormDTO();
        formDTO.setValues(frmValues);
        formDTO.setVariables(getVariables());
        formDTO.setId(getId());
        formDTO.setSurveyId(getSurveyId());
        formDTO.setFinish(finish);
        formDTO.setEnded(ended);
        formDTO.setQualified(qualified);
        formDTO.setCreated(created);
        formDTO.setUpdated(updated);
        return formDTO;
    }

    public String getClassName() {
        return getClass().getCanonicalName();
    }

    public boolean isDevMode() {
        return devMode;
    }

    public void setDevMode(boolean devMode) {
        this.devMode = devMode;
    }

    public boolean isFinish() {
        return finish;
    }

    public void setFinish(boolean finish) {
        this.finish = finish;
    }

    public Date getEnded() {
        return ended;
    }

    public void setEnded(Date ended) {
        this.ended = ended;
    }

    public boolean isQualified() {
        return qualified;
    }

    public void setQualified(boolean qualified) {
        this.qualified = qualified;
    }

    public Date getCreated() {
        return created;
    }

    public void setCreated(Date created) {
        this.created = created;
    }

    public Date getUpdated() {
        return updated;
    }

    public void setUpdated(Date updated) {
        this.updated = updated;
    }

    public String[] getCurrentQuestionsNames() {
        String[] questionNames = (String[]) variables.get("questionNames");
        if (questionNames != null) {
            return questionNames;
        }
        return new String[] {};
    }

    public Map<String, String> getRenderedKeys() {
        return renderedKeys;
    }
}