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