001/*
002 *  Copyright 2011 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.survey.answer;
017
018import java.net.URI;
019import java.net.URISyntaxException;
020import java.text.DateFormat;
021import java.util.ArrayList;
022import java.util.Arrays;
023import java.util.Collection;
024import java.util.Collections;
025import java.util.Date;
026import java.util.HashMap;
027import java.util.HashSet;
028import java.util.Iterator;
029import java.util.LinkedHashMap;
030import java.util.List;
031import java.util.Locale;
032import java.util.Map;
033import java.util.Set;
034import java.util.regex.Pattern;
035
036import org.apache.avalon.framework.parameters.Parameters;
037import org.apache.avalon.framework.service.ServiceException;
038import org.apache.avalon.framework.service.ServiceManager;
039import org.apache.cocoon.acting.ServiceableAction;
040import org.apache.cocoon.environment.Cookie;
041import org.apache.cocoon.environment.ObjectModelHelper;
042import org.apache.cocoon.environment.Redirector;
043import org.apache.cocoon.environment.Request;
044import org.apache.cocoon.environment.Response;
045import org.apache.cocoon.environment.SourceResolver;
046import org.apache.cocoon.environment.http.HttpCookie;
047import org.apache.commons.lang.StringUtils;
048
049import org.ametys.core.right.RightManager;
050import org.ametys.core.user.CurrentUserProvider;
051import org.ametys.core.user.UserIdentity;
052import org.ametys.plugins.repository.AmetysObjectIterable;
053import org.ametys.plugins.repository.AmetysObjectResolver;
054import org.ametys.plugins.repository.UnknownAmetysObjectException;
055import org.ametys.plugins.survey.data.SurveyAnswer;
056import org.ametys.plugins.survey.data.SurveyAnswerDao;
057import org.ametys.plugins.survey.data.SurveySession;
058import org.ametys.plugins.survey.repository.Survey;
059import org.ametys.plugins.survey.repository.SurveyAccessHelper;
060import org.ametys.plugins.survey.repository.SurveyPage;
061import org.ametys.plugins.survey.repository.SurveyQuestion;
062import org.ametys.plugins.survey.repository.SurveyRule;
063import org.ametys.runtime.i18n.I18nizableText;
064import org.ametys.web.URIPrefixHandler;
065import org.ametys.web.repository.page.Page;
066
067/**
068 * Process the user answers to the survey.
069 */
070public class ProcessInputAction extends ServiceableAction
071{
072    
073    /** The name of the cookie storing the taken surveys. */
074    public static final String COOKIE_NAME = "org.ametys.survey.takenSurveys";
075    
076    /** The ametys object resolver. */
077    protected AmetysObjectResolver _resolver;
078    /** The ametys object resolver. */
079    protected SurveyAnswerDao _answerDao;
080    /** The user provider. */
081    protected CurrentUserProvider _userProvider;
082    /** The uri prefix handler. */
083    protected URIPrefixHandler _prefixHandler;
084    /** The survey access helper */
085    protected SurveyAccessHelper _accessHelper;
086    
087    /** The plugin name */
088    protected String _pluginName;
089
090    private RightManager _rightManager;
091    
092    @Override
093    public void service(ServiceManager serviceManager) throws ServiceException
094    {
095        super.service(serviceManager);
096        _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE);
097        _answerDao = (SurveyAnswerDao) serviceManager.lookup(SurveyAnswerDao.ROLE);
098        _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE);
099        _prefixHandler = (URIPrefixHandler) serviceManager.lookup(URIPrefixHandler.ROLE);
100        _accessHelper = (SurveyAccessHelper) serviceManager.lookup(SurveyAccessHelper.ROLE);
101        _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE);
102    }
103    
104    @Override
105    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
106    {
107        Request request = ObjectModelHelper.getRequest(objectModel);
108        Response response = ObjectModelHelper.getResponse(objectModel);
109        
110        _pluginName = getPluginName(request);
111        
112        String surveyId = request.getParameter("surveyId");
113        if (StringUtils.isNotEmpty(surveyId))
114        {
115            // Get the survey object.
116            Survey survey = _resolver.resolveById(surveyId);
117            
118            SurveyErrors errors = new SurveyErrors();
119            
120            if (!checkAccess(survey, request, errors))
121            {
122                request.setAttribute("survey", survey);
123                request.setAttribute("survey-errors", errors);
124                return null;
125            }
126            
127            // Get the user input.
128            SurveyInput surveyInput = getInput(survey, request);
129            
130            // Validate the user input.
131            validateInput(survey, surveyInput, errors, request);
132            
133            // If there were errors in the input, store it as a request attribute and stop.
134            if (errors.hasErrors())
135            {
136                request.setAttribute("survey", survey);
137                request.setAttribute("survey-errors", errors);
138                return null;
139            }
140            
141            // Add the user session into the database.
142            _answerDao.addSession(surveyInput);
143            
144            setCookie(request, response, surveyId);
145            
146            // Redirect if necessary.
147            String redirectPageId = survey.getRedirection();
148            if (StringUtils.isNotEmpty(redirectPageId))
149            {
150                try
151                {
152                    Page page = _resolver.resolveById(redirectPageId);
153                    redirector.globalRedirect(false, _prefixHandler.getAbsoluteUriPrefix(page.getSiteName()) + "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + ".html");
154                }
155                catch (UnknownAmetysObjectException e)
156                {
157                    getLogger().warn("The survey '" + survey.getId() + "' wants to redirect to the unexisting page '" + redirectPageId + "'. Redirecting to default page.", e);
158                }
159            }
160        }
161        
162        return EMPTY_MAP;
163    }
164    
165    /**
166     * Get the plugin name
167     * @param request The request
168     * @return The plugin name
169     */
170    protected String getPluginName (Request request)
171    {
172        return (String) request.getAttribute("pluginName");
173    }
174    
175    /**
176     * Check if user can answer to the survey
177     * @param survey The survey
178     * @param request The request
179     * @param errors The survey errors
180     * @return false if the access failed
181     */
182    protected boolean checkAccess(Survey survey, Request request, SurveyErrors errors)
183    {
184        UserIdentity user = getAuthenticatedUser(request);
185        
186        if (!_rightManager.hasReadAccess(user, survey))
187        {
188            // User is not authorized
189            errors.addErrors("survey-access", Collections.singletonList(new I18nizableText("plugin." + _pluginName, "PLUGINS_SURVEY_RENDER_UNAUTHORIZED")));
190            return false;
191        }
192        
193        String surveyId = survey.getId();
194        Date submissionDate = _accessHelper.getSubmissionDate(surveyId, user);
195        
196        if (submissionDate != null)
197        {
198            // The authenticated user has already answered to the survey
199            Map<String, I18nizableText> i18nParams = new HashMap<>();
200            DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, new Locale(survey.getLanguage()));
201            i18nParams.put("date", new I18nizableText(df.format(submissionDate)));
202            
203            errors.addErrors("survey-access", Collections.singletonList(new I18nizableText("plugin." + _pluginName, "PLUGINS_SURVEY_RENDER_ALREADY_TAKEN_ON", i18nParams)));
204            return false;
205        }
206        
207        // Finally check cookies
208        if (_accessHelper.getCookieName(request, surveyId) != null)
209        {
210            // Anonymous user has already answered to the survey
211            errors.addErrors("survey-access", Collections.singletonList(new I18nizableText("plugin." + _pluginName, "PLUGINS_SURVEY_RENDER_ALREADY_TAKEN")));
212            return false;
213        }
214        
215        // User can access to the survey
216        return true;
217    }
218    
219    /**
220     * Get the authenticated user
221     * @param request The request
222     * @return The authenticated user
223     */
224    protected UserIdentity getAuthenticatedUser (Request request)
225    {
226        return _userProvider.getUser();
227    }
228    
229    /**
230     * Get the user input.
231     * @param survey the survey.
232     * @param request the request.
233     * @return the user input.
234     */
235    protected SurveyInput getInput(Survey survey, Request request)
236    {
237        String clientIp = getClientIp(request);
238        UserIdentity user = getAuthenticatedUser(request);
239        
240        SurveyInput surveySession = new SurveyInput();
241        
242        List<SurveyInputAnswer> answers = new ArrayList<>();
243        
244        surveySession.setSurveyId(survey.getId());
245        surveySession.setSubmittedAt(new Date());
246        surveySession.setUser(user);
247        surveySession.setIpAddress(clientIp);
248        surveySession.setAnswerList(answers);
249        
250        try (AmetysObjectIterable<SurveyQuestion> questions = survey.getQuestions())
251        {
252            for (SurveyQuestion question : questions)
253            {
254                Map<String, Set<String>> values = getValues(question, request);
255                
256                SurveyInputAnswer answer = new SurveyInputAnswer(question, values);
257                answers.add(answer);
258            }
259            
260            return surveySession;
261        }
262    }
263    
264    /**
265     * Get an answer value from the request.
266     * @param question the corresponding question.
267     * @param request the request.
268     * @return the answer value.
269     */
270    protected Map<String, Set<String>> getValues(SurveyQuestion question, Request request)
271    {
272        Map<String, Set<String>> values = new LinkedHashMap<>();
273        
274        String name = question.getName();
275        switch (question.getType())
276        {
277            case SINGLE_MATRIX:
278            case MULTIPLE_MATRIX:
279                Collection<String> options = question.getOptions().keySet();
280                
281                for (String option : options)
282                {
283                    String paramName = name + "_" + option;
284                    String[] paramValues = request.getParameterValues(paramName);
285                    
286                    if (paramValues != null)
287                    {
288                        values.put(option, new HashSet<>(Arrays.asList(paramValues)));
289                    }
290                }
291                break;
292            case FREE_TEXT:
293            case MULTILINE_FREE_TEXT:
294                String[] textValues = request.getParameterValues(name);
295                if (textValues != null)
296                {
297                    values.put("values", new HashSet<>(Arrays.asList(textValues)));
298                }
299                break;
300            case SINGLE_CHOICE:
301            case MULTIPLE_CHOICE:
302            default:
303                List<String> valuesAsList = new ArrayList<>();
304                String[] paramValues = request.getParameterValues(name);
305                if (paramValues != null)
306                {
307                    for (String value : paramValues)
308                    {
309                        if (value.equals("__internal_other"))
310                        {
311                            valuesAsList.add(request.getParameter("__internal_other_" + name));
312                        }
313                        else
314                        {
315                            valuesAsList.add(value);
316                        }
317                    }
318                    values.put("values", new HashSet<>(valuesAsList));
319                }
320                break;
321        }
322        
323        return values;
324    }
325    
326    /**
327     * Validate the user input.
328     * @param survey the survey.
329     * @param input the user input.
330     * @param errors the errors.
331     * @param request the request.
332     */
333    protected void validateInput(Survey survey, SurveyInput input, SurveyErrors errors, Request request)
334    {
335        SurveyRule ruleToExecute = null;
336        
337        Map<String, SurveyInputAnswer> answers = input.getAnswerMap();
338        
339        for (SurveyPage page : survey.getPages())
340        {
341            if (ruleToExecute == null || processPage(page, ruleToExecute))
342            {
343                // Reset the current rule.
344                ruleToExecute = null;
345                
346                AmetysObjectIterable<SurveyQuestion> questions = page.getQuestions();
347                
348                for (SurveyQuestion question : questions)
349                {
350                    SurveyInputAnswer answer = answers.get(question.getName());
351                    
352                    switch (question.getType())
353                    {
354                        case FREE_TEXT:
355                        case MULTILINE_FREE_TEXT:
356                            errors.addErrors(question.getName(), validateText(answer, request));
357                            ruleToExecute = evaluateTextRules(question, answer);
358                            break;
359                        case SINGLE_CHOICE:
360                        case MULTIPLE_CHOICE:
361                            errors.addErrors(question.getName(), validateChoice(answer, request));
362                            ruleToExecute = evaluateChoiceRules(question, answer);
363                            break;
364                        case SINGLE_MATRIX:
365                        case MULTIPLE_MATRIX:
366                            errors.addErrors(question.getName(), validateMatrix(answer, request));
367                            ruleToExecute = evaluateMatrixRules(question, answer);
368                            break;
369                        default:
370                            break;
371                    }
372                }
373                
374                SurveyRule pageRule = page.getRule();
375                
376                if (ruleToExecute == null && pageRule != null)
377                {
378                    ruleToExecute = pageRule;
379                }
380            }
381        }
382    }
383    
384    /**
385     * Test if a page is to be processed, depending on the rule.
386     * @param page the page to test.
387     * @param rule the rule to execute.
388     * @return true to process the page, false otherwise.
389     */
390    protected boolean processPage(SurveyPage page, SurveyRule rule)
391    {
392        boolean processPage = false;
393        
394        switch (rule.getType())
395        {
396            case JUMP:
397                // If the page is the targeted page, it passes the condition.
398                processPage = page.getId().equals(rule.getPage());
399                break;
400            case SKIP:
401                // If the page is the skipped page, it is not displayed.
402                processPage = !page.getId().equals(rule.getPage());
403                break;
404            case FINISH:
405                // When finished, no more page is displayed.
406                break;
407            default:
408                break;
409        }
410        
411        return processPage;
412    }
413    
414    /**
415     * Evaluate rules on a text question.
416     * @param question the text question.
417     * @param answer the user answer to the question.
418     * @return the matched rule.
419     */
420    protected SurveyRule evaluateTextRules(SurveyQuestion question, SurveyInputAnswer answer)
421    {
422        return null;
423    }
424    
425    /**
426     * Evaluate rules on a choice question.
427     * @param question the choice question.
428     * @param answer the user answer to the question.
429     * @return the matched rule.
430     */
431    protected SurveyRule evaluateChoiceRules(SurveyQuestion question, SurveyInputAnswer answer)
432    {
433        SurveyRule matchedRule = null;
434        
435        Map<String, Set<String>> valueMap = answer.getValuesMap();
436        if (valueMap.containsKey("values"))
437        {
438            Set<String> values = answer.getValuesMap().get("values");
439            
440            Iterator<SurveyRule> questionRules = question.getRules().iterator();
441            while (questionRules.hasNext() && matchedRule == null)
442            {
443                SurveyRule rule = questionRules.next();
444                
445                if (values.contains(rule.getOption()))
446                {
447                    // Condition met, store the action.
448                    matchedRule = rule;
449                }
450            }
451        }
452        
453        return matchedRule;
454    }
455    
456    /**
457     * Evaluate rules on a matrix question.
458     * @param question the matrix question.
459     * @param answer the user answer to the question.
460     * @return the matched rule.
461     */
462    protected SurveyRule evaluateMatrixRules(SurveyQuestion question, SurveyInputAnswer answer)
463    {
464        return null;
465    }
466    
467    /**
468     * Validate a text field.
469     * @param answer the user answer to the question.
470     * @param request the request.
471     * @return the error list.
472     */
473    protected List<I18nizableText> validateText(SurveyInputAnswer answer, Request request)
474    {
475        List<I18nizableText> errors = new ArrayList<>();
476        
477        SurveyQuestion question = answer.getQuestion();
478        
479        boolean isBlank = isBlank(answer);
480        
481        final String textPrefix = "PLUGINS_SURVEY_RENDER_ERROR_TEXT_";
482        
483        if (question.isMandatory() && isBlank)
484        {
485            errors.add(new I18nizableText("plugin." + _pluginName, textPrefix + "MANDATORY"));
486        }
487        
488        if (!isBlank)
489        {
490            errors.addAll(validatePattern(answer, textPrefix));
491        }
492        
493        return errors;
494    }
495    
496    /**
497     * Validate a choice question answer.
498     * @param answer the user answer to the question.
499     * @param request the request.
500     * @return the error list.
501     */
502    protected List<I18nizableText> validateChoice(SurveyInputAnswer answer, Request request)
503    {
504        List<I18nizableText> errors = new ArrayList<>();
505        
506        SurveyQuestion question = answer.getQuestion();
507        
508        boolean isBlank = isBlank(answer);
509        
510        final String textPrefix = "PLUGINS_SURVEY_RENDER_ERROR_CHOICE_";
511        
512        if (question.isMandatory() && isBlank)
513        {
514            errors.add(new I18nizableText("plugin." + _pluginName, textPrefix + "MANDATORY"));
515        }
516        
517        return errors;
518    }
519    
520    /**
521     * Validate a matrix question answer.
522     * @param answer the user answer to the question.
523     * @param request the request.
524     * @return the error list.
525     */
526    protected List<I18nizableText> validateMatrix(SurveyInputAnswer answer, Request request)
527    {
528        List<I18nizableText> errors = new ArrayList<>();
529        
530        SurveyQuestion question = answer.getQuestion();
531        
532        boolean isBlank = isBlank(answer);
533        
534        final String textPrefix = "PLUGINS_SURVEY_RENDER_ERROR_MATRIX_";
535        
536        if (question.isMandatory() && isBlank)
537        {
538            errors.add(new I18nizableText("plugin." + _pluginName, textPrefix + "MANDATORY"));
539        }
540        
541        return errors;
542    }
543    
544    /**
545     * Validate an answer against a pattern.
546     * @param answer the user answer to the question.
547     * @param keyPrefix the error i18n key prefix.
548     * @return the error list.
549     */
550    protected List<I18nizableText> validatePattern(SurveyInputAnswer answer, String keyPrefix)
551    {
552        List<I18nizableText> errors = new ArrayList<>();
553        
554        SurveyQuestion question = answer.getQuestion();
555        String regex = question.getRegExpPattern();
556        String value = answer.getValue();
557        
558        if (StringUtils.isNotBlank(regex))
559        {
560            if (!Pattern.matches(regex, value))
561            {
562                errors.add(new I18nizableText("plugin." + _pluginName, keyPrefix + "PATTERN"));
563            }
564        }
565        
566        return errors;
567    }
568    
569    /**
570     * Test if the answer is empty.
571     * @param answer the user answer.
572     * @return true if the answer is empty.
573     */
574    protected boolean isBlank(SurveyInputAnswer answer)
575    {
576        boolean blank = answer.getValuesMap().isEmpty();
577        
578        for (Set<String> values : answer.getValuesMap().values())
579        {
580            if (StringUtils.isEmpty(StringUtils.join(values, "")))
581            {
582                return true;
583            }
584        }
585        
586        return blank;
587    }
588    
589    /**
590     * Indicate in a cookie that the survey was taken.
591     * @param request the request.
592     * @param response the response.
593     * @param surveyId the ID of the survey that was just taken.
594     */
595    protected void setCookie(Request request, Response response, String surveyId)
596    {
597        Map<String, Cookie> cookieMap = request.getCookieMap();
598        
599        Cookie cookie = null;
600        if (cookieMap.containsKey(COOKIE_NAME))
601        {
602            cookie = cookieMap.get(COOKIE_NAME);
603            String newValue = cookie.getValue() + "," + surveyId;
604            cookie.setValue(newValue);
605        }
606        else
607        {
608            cookie = response.createCookie(COOKIE_NAME, surveyId);
609            cookie.setSecure(request.isSecure());
610            ((HttpCookie) cookie).getServletCookie().setHttpOnly(false); // Cookie is also manipulated by the javascript
611        }
612        
613        cookie.setVersion(1);
614        cookie.setMaxAge(30 * 24 * 3600); // 30 days
615        
616        String absPrefix = _prefixHandler.getAbsoluteUriPrefix();
617        
618        String host = null;
619        
620        try
621        {
622            URI frontUri = new URI(absPrefix);
623            host = frontUri.getHost();
624            String path = frontUri.getPath();
625            cookie.setPath(StringUtils.isEmpty(path) ? "/" : path);
626        }
627        catch (URISyntaxException e)
628        {
629            getLogger().warn("The front URI seems to be invalid.", e);
630        }
631        
632        if (StringUtils.isNotEmpty(host))
633        {
634            cookie.setDomain(host);
635            response.addCookie(cookie);
636        }
637    }
638    
639    /**
640     * Get a forwarded client IP address if available.
641     * @param request The servlet request object.
642     * @return The HTTP <code>X-Forwarded-For</code> header value if present,
643     * or the default remote address if not.
644     */
645    protected final String getClientIp(final Request request)
646    {
647        String ip = "";
648        
649        String forwardedIpHeader = request.getHeader("X-Forwarded-For");
650        
651        if (StringUtils.isNotEmpty(forwardedIpHeader))
652        {
653            String[] forwardedIps = forwardedIpHeader.split("[\\s,]+");
654            String forwardedIp = forwardedIps[forwardedIps.length - 1];
655            if (StringUtils.isNotBlank(forwardedIp))
656            {
657                ip = forwardedIp.trim();
658            }
659        }
660        
661        if (StringUtils.isBlank(ip))
662        {
663            ip = request.getRemoteAddr();
664        }
665        
666        return ip;
667    }
668    
669    /**
670     * Survey session with answers.
671     */
672    protected class SurveyInput extends SurveySession
673    {
674        /** Answers. */
675        protected List<SurveyInputAnswer> _inputAnswers;
676        
677        @Override
678        public List<SurveyInputAnswer> getAnswers()
679        {
680            return _inputAnswers;
681        }
682        
683        /**
684         * Get the answers as a Map indexed by question ID.
685         * @return the answer Map.
686         */
687        public Map<String, SurveyInputAnswer> getAnswerMap()
688        {
689            Map<String, SurveyInputAnswer> answerMap = new LinkedHashMap<>();
690            
691            for (SurveyInputAnswer answer : _inputAnswers)
692            {
693                answerMap.put(answer.getQuestionId(), answer);
694            }
695            
696            return answerMap;
697        }
698        
699        /**
700         * Set the answers.
701         * @param answers the answers to set
702         */
703        public void setAnswerList(List<SurveyInputAnswer> answers)
704        {
705            this._inputAnswers = answers;
706        }
707    }
708    
709    /**
710     * Class representing a survey answer, i.e. the response of a user to a question of the survey.
711     */
712    protected class SurveyInputAnswer extends SurveyAnswer
713    {
714        
715        /** The question. */
716        protected SurveyQuestion _question;
717        
718        /** The answer values. */
719        protected Map<String, Set<String>> _values;
720        
721        /**
722         * Build a SurveyAnswer object.
723         */
724        public SurveyInputAnswer()
725        {
726            this(null, null);
727        }
728        
729        /**
730         * Build a SurveyAnswer object.
731         * @param question the question ID.
732         * @param values the answer value.
733         */
734        public SurveyInputAnswer(SurveyQuestion question, Map<String, Set<String>> values)
735        {
736            this._question = question;
737            this._values = values;
738        }
739        
740        /**
741         * Get the question.
742         * @return the question
743         */
744        public SurveyQuestion getQuestion()
745        {
746            return _question;
747        }
748        
749        /**
750         * Set the question.
751         * @param question the question to set
752         */
753        public void setQuestion(SurveyQuestion question)
754        {
755            this._question = question;
756        }
757        
758        @Override
759        public String getQuestionId()
760        {
761            return _question.getName();
762        }
763        
764        @Override
765        public void setQuestionId(String questionId)
766        {
767            throw new IllegalAccessError("Set the question instead of the question ID.");
768        }
769        
770        /**
771         * Get the values.
772         * @return the values
773         */
774        public Map<String, Set<String>> getValuesMap()
775        {
776            return _values;
777        }
778        
779        /**
780         * Set the values.
781         * @param values the values to set
782         */
783        public void setValueMap(Map<String, Set<String>> values)
784        {
785            this._values = values;
786        }
787        
788        @Override
789        public String getValue()
790        {
791            String value = "";
792            
793            switch (_question.getType())
794            {
795                case SINGLE_MATRIX:
796                case MULTIPLE_MATRIX:
797                    StringBuilder valueBuff = new StringBuilder();
798                    
799                    for (String option : _values.keySet())
800                    {
801                        Set<String> values = _values.get(option);
802                        
803                        if (valueBuff.length() > 0)
804                        {
805                            valueBuff.append(";");
806                        }
807                        valueBuff.append(option).append(":").append(StringUtils.join(values, ","));
808                    }
809                    
810                    value = valueBuff.toString();
811                    break;
812                case FREE_TEXT:
813                case MULTILINE_FREE_TEXT:
814                case SINGLE_CHOICE:
815                case MULTIPLE_CHOICE:
816                default:
817                    value = StringUtils.defaultString(StringUtils.join(_values.get("values"), ","));
818                    break;
819            }
820            
821            return value;
822        }
823        
824        @Override
825        public void setValue(String value)
826        {
827            throw new IllegalAccessError("Set the value map instead of the vlaue.");
828        }
829        
830    }
831
832}