001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.xml.xml2json;
029
030import org.opencms.json.JSONException;
031import org.opencms.json.JSONObject;
032import org.opencms.jsp.search.config.parser.CmsSimpleSearchConfigurationParser.SortOption;
033import org.opencms.xml.xml2json.handler.CmsJsonHandlerContainerPage;
034import org.opencms.xml.xml2json.handler.CmsJsonHandlerContext;
035import org.opencms.xml.xml2json.handler.CmsJsonHandlerFolder;
036import org.opencms.xml.xml2json.handler.CmsJsonHandlerList;
037import org.opencms.xml.xml2json.handler.CmsJsonHandlerResource;
038import org.opencms.xml.xml2json.handler.CmsJsonHandlerXmlContent;
039import org.opencms.xml.xml2json.handler.I_CmsJsonHandler;
040
041import java.util.ArrayList;
042import java.util.Arrays;
043import java.util.List;
044
045import javax.servlet.http.HttpServletResponse;
046
047/**
048 * Class representing a JSON request. Provides utility functions for parameter validation.
049 */
050public class CmsJsonRequest {
051
052    /** The content request parameter. */
053    public static final String PARAM_CONTENT = "content";
054
055    /** The fallback locale request parameter. */
056    public static final String PARAM_FALLBACK_LOCALE = "fallbackLocale";
057
058    /** The levels request parameter. */
059    public static final String PARAM_LEVELS = "levels";
060
061    /** The locale request parameter. */
062    public static final String PARAM_LOCALE = "locale";
063
064    /** The path request parameter. */
065    public static final String PARAM_PATH = "path";
066
067    /** The rows request parameter. */
068    public static final String PARAM_ROWS = "rows";
069
070    /** The sort request parameter. */
071    public static final String PARAM_SORT = "sort";
072
073    /** The start request parameter. */
074    public static final String PARAM_START = "start";
075
076    /** The wrapper request parameter. */
077    public static final String PARAM_WRAPPER = "wrapper";
078
079    /** The JSON handler context. */
080    CmsJsonHandlerContext m_context;
081
082    /** The JSON handler initiating this request.*/
083    I_CmsJsonHandler m_handler;
084
085    /** The list of validation errors. */
086    List<String> errors = new ArrayList<String>();
087
088    /**
089     * Creates a new JSON request.<p>
090     *
091     * @param context the JSON handler context
092     * @param handler the JSON handler initiating this request
093     */
094    public CmsJsonRequest(CmsJsonHandlerContext context, I_CmsJsonHandler handler) {
095
096        m_context = context;
097        m_handler = handler;
098    }
099
100    /**
101     * Returns the JSON handler context.<p>
102     *
103     * @return the JSON handler context
104     */
105    public CmsJsonHandlerContext getContext() {
106
107        return m_context;
108    }
109
110    /**
111     * Returns the errors of this request as JSON.<p>
112     *
113     * @return the errors as JSON
114     * @throws JSONException if JSON rendering fails
115     */
116    public JSONObject getErrorsAsJson() throws JSONException {
117
118        JSONObject jsonObject = new JSONObject();
119        jsonObject.put("status", HttpServletResponse.SC_BAD_REQUEST);
120        for (String error : errors) {
121            jsonObject.append("errors", error);
122        }
123        return jsonObject;
124    }
125
126    /**
127     * Returns the boolean parameter value for a given string.<p>
128     *
129     * @param bool the string
130     * @return the boolean
131     */
132    public Boolean getParamBoolean(String bool) {
133
134        if ((bool == null) || bool.equals("false")) {
135            return Boolean.valueOf(false);
136        } else {
137            return Boolean.valueOf(true);
138        }
139    }
140
141    /**
142     * Returns the content parameter as boolean.<p>
143     *
144     * @return the content parameter as boolean
145     */
146    public Boolean getParamContent() {
147
148        String paramContent = m_context.getParameters().get(PARAM_CONTENT);
149        return getParamBoolean(paramContent);
150    }
151
152    /**
153     * Returns the fallback locale parameter as boolean.<p>
154     *
155     * @return the fallback locale parameter as boolean
156     */
157    public Boolean getParamFallbackLocale() {
158
159        String paramFallbackLocale = m_context.getParameters().get(PARAM_FALLBACK_LOCALE);
160        return getParamBoolean(paramFallbackLocale);
161    }
162
163    /**
164     * Returns the levels parameter as integer.<p>
165     *
166     * @return the levels parameter as integer
167     */
168    public Integer getParamLevels() {
169
170        String paramLevels = m_context.getParameters().get(PARAM_LEVELS);
171        return paramLevels != null ? Integer.valueOf(paramLevels) : null;
172    }
173
174    /**
175     * Returns the levels parameter as integer.<p>
176     *
177     * @param defaultLevels if parameter is not set return this default value
178     * @return the levels parameter as integer
179     */
180    public Integer getParamLevels(int defaultLevels) {
181
182        Integer levels = getParamLevels();
183        return levels != null ? levels : Integer.valueOf(defaultLevels);
184    }
185
186    /**
187     * Returns the locale parameter as string.<p>
188     *
189     * @return the levels parameter as integer
190     */
191    public String getParamLocale() {
192
193        String paramLocale = m_context.getParameters().get(PARAM_LOCALE);
194        return paramLocale;
195    }
196
197    /**
198     * Returns the path parameter as string.<p>
199     *
200     * @return the path parameter as string
201     */
202    public String getParamPath() {
203
204        String paramPath = m_context.getParameters().get(PARAM_PATH);
205        return paramPath;
206    }
207
208    /**
209     * Returns the rows parameter as integer.<p>
210     *
211     * @return the rows parameter as integer
212     */
213    public Integer getParamRows() {
214
215        String paramRows = m_context.getParameters().get(PARAM_ROWS);
216        return paramRows != null ? Integer.valueOf(paramRows) : null;
217    }
218
219    /**
220     * Returns the rows parameter as integer.<p>
221     *
222     * @param defaultRows if parameter is not set return this default value
223     * @return the rows parameter as integer
224     */
225    public Integer getParamRows(int defaultRows) {
226
227        Integer rows = getParamStart();
228        return rows != null ? rows : Integer.valueOf(defaultRows);
229    }
230
231    /**
232     * Returns the sort parameter as string.<p>
233     *
234     * @return the sort parameter as string
235     */
236    public String getParamSort() {
237
238        return m_context.getParameters().get(PARAM_SORT);
239    }
240
241    /**
242     * Returns the sort parameter as string.<p>
243     *
244     * @param defaultSort if parameter is not set return this default value
245     * @return the sort parameter as string
246     */
247    public String getParamSort(String defaultSort) {
248
249        String sort = getParamSort();
250        return sort != null ? sort : defaultSort;
251    }
252
253    /**
254     * Returns the start parameter as integer.<p>
255     *
256     * @return the start parameter as integer
257     */
258    public Integer getParamStart() {
259
260        String paramStart = m_context.getParameters().get(PARAM_START);
261        return paramStart != null ? Integer.valueOf(paramStart) : null;
262    }
263
264    /**
265     * Returns the rows parameter as integer.<p>
266     *
267     * @param defaultStart if parameter is not set return this default value
268     * @return the rows parameter as integer
269     */
270    public Integer getParamStart(int defaultStart) {
271
272        Integer start = getParamStart();
273        return start != null ? start : Integer.valueOf(defaultStart);
274    }
275
276    /**
277     * Returns the wrapper parameter as string.<p>
278     *
279     * @return the wrapper parameter as string
280     */
281    public Boolean getParamWrapper() {
282
283        String paramWrapper = m_context.getParameters().get(PARAM_WRAPPER);
284        return getParamBoolean(paramWrapper);
285    }
286
287    /**
288     * Returns the wrapper parameter as string.<p>
289     *
290     * @param defaultWrapper if parameter is not set return this default value
291     * @return the wrapper parameter as string
292     */
293    public Boolean getParamWrapper(boolean defaultWrapper) {
294
295        String paramRaw = m_context.getParameters().get(PARAM_WRAPPER);
296        if (paramRaw == null) {
297            return Boolean.valueOf(defaultWrapper);
298        }
299        return getParamWrapper();
300    }
301
302    /**
303     * Whether this JSON request has validation errors.
304     *
305     * @return whether has validation errors or not
306     */
307    public boolean hasErrors() {
308
309        return !errors.isEmpty();
310    }
311
312    /**
313     * Validates this request.<p>
314     */
315    public void validate() {
316
317        validateRequest();
318        validateDependencies();
319        validateParamBooleanValue(PARAM_CONTENT, true);
320        validateParamBooleanValue(PARAM_FALLBACK_LOCALE, true);
321        validateParamPositiveIntegerValue(PARAM_LEVELS, false);
322        validateParamPositiveIntegerValue(PARAM_ROWS, false);
323        validateParamSortValue(PARAM_SORT, false);
324        validateParamPositiveIntegerValue(PARAM_START, false);
325        validateParamBooleanValue(PARAM_WRAPPER, true);
326    }
327
328    /**
329     * Validates all parameter dependencies.
330     */
331    private void validateDependencies() {
332
333        validateParamDepends(PARAM_FALLBACK_LOCALE, PARAM_LOCALE);
334        validateParamDepends(PARAM_PATH, PARAM_LOCALE);
335        validateParamDepends(PARAM_ROWS, PARAM_CONTENT);
336        validateParamDepends(PARAM_SORT, PARAM_CONTENT);
337        validateParamDepends(PARAM_START, PARAM_CONTENT);
338    }
339
340    /**
341     * Validates a boolean request parameter.<p>
342     *
343     * @param paramName the name of the parameter to be validated
344     * @param maybeEmpty whether the parameter value may be empty
345     */
346    private void validateParamBooleanValue(String paramName, boolean maybeEmpty) {
347
348        String paramValue = m_context.getParameters().get(paramName);
349        if (paramValue != null) {
350            if (maybeEmpty && paramValue.equals("")) {
351                return;
352            }
353            if (!(paramValue.equals("true") || paramValue.equals("false"))) {
354                errors.add(
355                    "<" + paramValue + "> is not a boolean. Boolean expected for parameter <" + paramName + ">.");
356            }
357        }
358    }
359
360    /**
361     * For two parameters depending on each other, validates whether the
362     * dependency is fulfilled for the current request.
363     *
364     * @param param the first parameter
365     * @param other the second parameter which the first parameter depends on
366     */
367    private void validateParamDepends(String param, String other) {
368
369        String first = m_context.getParameters().get(param);
370        String second = m_context.getParameters().get(other);
371        if ((first != null) && (second == null)) {
372            errors.add("Parameter <" + param + "> depends on parameter <" + other + ">. <" + other + "> is expected.");
373        }
374    }
375
376    /**
377     * Validates an integer request parameter.<p>
378     *
379     * @param paramName the name of the parameter to be validated
380     * @param maybeEmpty whether the parameter value may be empty
381     */
382    private void validateParamPositiveIntegerValue(String paramName, boolean maybeEmpty) {
383
384        String paramValue = m_context.getParameters().get(paramName);
385        if (paramValue != null) {
386            if (maybeEmpty && paramValue.equals("")) {
387                return;
388            }
389            String message = "<"
390                + paramValue
391                + "> is not a positive integer. Positive integer expected for parameter <"
392                + paramName
393                + ">.";
394            try {
395                if (Integer.valueOf(paramValue).intValue() < 0) {
396                    errors.add(message);
397                }
398            } catch (NumberFormatException e) {
399                errors.add(message);
400            }
401        }
402    }
403
404    /**
405     * Validates a sort request parameter.<p>
406     *
407     * @param paramName the name of the parameter to be validated
408     * @param maybeEmpty whether the parameter value may be empty
409     */
410    private void validateParamSortValue(String paramName, boolean maybeEmpty) {
411
412        String paramValue = m_context.getParameters().get(paramName);
413        if (paramValue != null) {
414            if (maybeEmpty && paramValue.equals("")) {
415                return;
416            }
417            List<SortOption> list = Arrays.asList(SortOption.values());
418            List<String> allowed = new ArrayList<String>();
419            for (SortOption sortOption : SortOption.values()) {
420                allowed.add(sortOption.toString());
421            }
422            String message = "<"
423                + paramValue
424                + "> is not a valid sort option. One of <"
425                + String.join(",", allowed)
426                + "> is expected.";
427            try {
428                if (!list.contains(SortOption.valueOf(paramValue))) {
429                    errors.add(message);
430                }
431            } catch (Exception e) {
432                errors.add(message);
433            }
434        }
435    }
436
437    /**
438     * For a given list of parameter names supported, validates whether the current
439     * parameters used in this request are valid.
440     *
441     * @param supported the parameters supported
442     */
443    private void validateParamsSupported(String[] supported) {
444
445        List<String> paramList = Arrays.asList(supported);
446        for (String paramName : m_context.getParameters().keySet()) {
447            if (!paramList.contains(paramName)) {
448                errors.add(
449                    "Parameter <"
450                        + paramName
451                        + "> is not supported for this request type. One of <"
452                        + String.join(",", supported)
453                        + "> is expected.");
454            }
455        }
456    }
457
458    /**
459     * Validates this request type.<p>
460     */
461    private void validateRequest() {
462
463        if (m_handler instanceof CmsJsonHandlerFolder) {
464            validateRequestFolder();
465        } else if (m_handler instanceof CmsJsonHandlerContainerPage) {
466            validateRequestContainerPage();
467        } else if (m_handler instanceof CmsJsonHandlerList) {
468            validateRequestList();
469        } else if (m_handler instanceof CmsJsonHandlerXmlContent) {
470            validateRequestXmlContent();
471        } else if (m_handler instanceof CmsJsonHandlerResource) {
472            validateRequestResource();
473        }
474    }
475
476    /**
477     * Validates the container page request type.<p>
478     */
479    private void validateRequestContainerPage() {
480
481        String[] supported = {PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE};
482        validateParamsSupported(supported);
483    }
484
485    /**
486     * Validates the folder request type.<p>
487     */
488    private void validateRequestFolder() {
489
490        String[] supported = {PARAM_LEVELS, PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE};
491        validateParamsSupported(supported);
492    }
493
494    /**
495     * Validates the list request type.<p>
496     */
497    private void validateRequestList() {
498
499        String[] supported = {
500            PARAM_CONTENT,
501            PARAM_WRAPPER,
502            PARAM_LOCALE,
503            PARAM_FALLBACK_LOCALE,
504            PARAM_START,
505            PARAM_ROWS,
506            PARAM_SORT};
507        validateParamsSupported(supported);
508    }
509
510    /**
511     * Validates the resource request type.<p>
512     */
513    private void validateRequestResource() {
514
515        String[] supported = {};
516        validateParamsSupported(supported);
517    }
518
519    /**
520     * Validates the XML content request type.<p>
521     */
522    private void validateRequestXmlContent() {
523
524        String[] supported = {PARAM_CONTENT, PARAM_WRAPPER, PARAM_LOCALE, PARAM_FALLBACK_LOCALE, PARAM_PATH};
525        validateParamsSupported(supported);
526    }
527}