Java tutorial
/* * 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; } }