001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.console.datatables;
018
019import java.util.*;
020
021import org.apache.juneau.commons.bean.*;
022
023/**
024 * The DataTables server-side-processing request envelope &mdash; the JSON <b>input</b> bean paired with the
025 * {@link DataTablesResults} JSON <b>output</b> bean.
026 *
027 * <p>
028 * This bean is a faithful model of the
029 * <a class="doclink" href="https://datatables.net/manual/server-side">DataTables server-side request</a>; it carries
030 * <b>no</b> query-engine types and knows nothing about {@code BeanQuery}.  The adapter in
031 * {@code org.apache.juneau.console.datatables.adapter} maps a parsed instance onto a
032 * {@link org.apache.juneau.commons.beanquery.BeanQuery}.
033 *
034 * <p>
035 * Shape (fields omitted from the request keep their defaults):
036 * <p class='bjson'>
037 *    {
038 *       <jf>draw</jf>: 1,
039 *       <jf>start</jf>: 0,
040 *       <jf>length</jf>: 10,
041 *       <jf>search</jf>: { <jf>value</jf>: <js>""</js>, <jf>regex</jf>: <jk>false</jk>, <jf>fixed</jf>: [] },
042 *       <jf>columns</jf>: [ { <jf>data</jf>: <js>"name"</js>, <jf>name</jf>: <js>""</js>, <jf>searchable</jf>: <jk>true</jk>, <jf>orderable</jf>: <jk>true</jk>, <jf>search</jf>: { <jf>value</jf>: <js>""</js>, <jf>regex</jf>: <jk>false</jk>, <jf>fixed</jf>: [] } } ],
043 *       <jf>order</jf>: [ { <jf>column</jf>: 0, <jf>dir</jf>: <js>"asc"</js>, <jf>name</jf>: <js>""</js> } ]
044 *    }
045 * </p>
046 *
047 * <p>
048 * Every property DataTables 2.x core sends is modelled (including {@code search.fixed} and {@code order[].name}, both new
049 * in 2.x), so the request parses under Juneau's default strict bean parsing, which rejects unknown properties.
050 * Any OTHER top-level property - one an application merges in through {@code JuneauDataTables.ajax(url, {data:{...}})},
051 * or one a DataTables extension adds (SearchBuilder's {@code searchBuilder}, SearchPanes' {@code searchPanes}) - is
052 * collected into {@link #getExtra()} instead of failing the parse.  The adapter ignores it.
053 *
054 * <h5 class='section'>Example:</h5>
055 * <p class='bjava'>
056 *    <ja>@RestPost</ja>(<js>"/releases/query"</js>)
057 *    <jk>public</jk> DataTablesResults&lt;Release&gt; queryReleases(<ja>@Content</ja> DataTablesRequest <jv>request</jv>) {
058 *       <jk>try</jk> (var <jv>session</jv> = <jsf>CONTEXT</jsf>.getSession(<jv>releases</jv>)) {
059 *          <jk>return</jk> DataTablesQuery.<jsm>run</jsm>(<jv>request</jv>, <jv>session</jv>);
060 *       }
061 *    }
062 * </p>
063 *
064 * @since 10.0.0
065 */
066public class DataTablesRequest {
067
068   private int draw;
069   private int start;
070   private int length = -1;  // -1 (or an absent length) means "all rows".
071   private Search search;
072   private List<Column> columns;
073   private List<Order> order;
074   private final Map<String,Object> extra = new LinkedHashMap<>();
075
076   /**
077    * Returns the draw counter echoed back on the response.
078    *
079    * @return The draw counter.
080    */
081   public int getDraw() {
082      return draw;
083   }
084
085   /**
086    * Sets the draw counter.
087    *
088    * @param value The value.
089    * @return This object.
090    */
091   public DataTablesRequest setDraw(int value) {
092      this.draw = value;
093      return this;
094   }
095
096   /**
097    * Returns the 0-based index of the first record to return.
098    *
099    * @return The paging start index.
100    */
101   public int getStart() {
102      return start;
103   }
104
105   /**
106    * Sets the 0-based index of the first record to return.
107    *
108    * @param value The value.
109    * @return This object.
110    */
111   public DataTablesRequest setStart(int value) {
112      this.start = value;
113      return this;
114   }
115
116   /**
117    * Returns the number of records the page should return ({@code -1} means all records, no paging).
118    *
119    * @return The page length.
120    */
121   public int getLength() {
122      return length;
123   }
124
125   /**
126    * Sets the number of records the page should return ({@code -1} means all records, no paging).
127    *
128    * @param value The value.
129    * @return This object.
130    */
131   public DataTablesRequest setLength(int value) {
132      this.length = value;
133      return this;
134   }
135
136   /**
137    * Returns the global search descriptor.
138    *
139    * @return The global search, or <jk>null</jk> if none.
140    */
141   public Search getSearch() {
142      return search;
143   }
144
145   /**
146    * Sets the global search descriptor.
147    *
148    * @param value The value.
149    * @return This object.
150    */
151   public DataTablesRequest setSearch(Search value) {
152      this.search = value;
153      return this;
154   }
155
156   /**
157    * Returns the column descriptors.
158    *
159    * @return The columns, or <jk>null</jk> if none.
160    */
161   public List<Column> getColumns() {
162      return columns;
163   }
164
165   /**
166    * Sets the column descriptors.
167    *
168    * @param value The value.
169    * @return This object.
170    */
171   public DataTablesRequest setColumns(List<Column> value) {
172      this.columns = value;
173      return this;
174   }
175
176   /**
177    * Returns the ordering descriptors.
178    *
179    * @return The order entries, or <jk>null</jk> if none.
180    */
181   public List<Order> getOrder() {
182      return order;
183   }
184
185   /**
186    * Sets the ordering descriptors.
187    *
188    * @param value The value.
189    * @return This object.
190    */
191   public DataTablesRequest setOrder(List<Order> value) {
192      this.order = value;
193      return this;
194   }
195
196   /**
197    * Returns every top-level request property that is not one of the modelled DataTables properties.
198    *
199    * <p>
200    * For example the {@code tenant} in {@code JuneauDataTables.ajax(url, {data:{tenant:'x'}})}, or an extension's
201    * own request key.  Lets a handler read application parameters that ride the JSON body.
202    *
203    * @return The extra properties, keyed by name.  Never <jk>null</jk>.
204    */
205   @BeanProp(name="*")
206   public Map<String,Object> getExtra() {
207      return extra;
208   }
209
210   /**
211    * Records a top-level request property that is not one of the modelled DataTables properties.
212    *
213    * @param name The property name.
214    * @param value The property value.
215    */
216   @BeanProp(name="*")
217   public void setExtra(String name, Object value) {
218      extra.put(name, value);
219   }
220
221   /**
222    * A DataTables search descriptor ({@code {value, regex, fixed}}), used for both the global search and a column
223    * search.
224    */
225   public static class Search {
226      private String value;
227      private boolean regex;
228      private List<Fixed> fixed;
229
230      /**
231       * Returns the DataTables 2.x fixed (named, API-applied) search terms.  Carried for wire fidelity &mdash; a
232       * strict parser would otherwise reject every DataTables 2.x request &mdash; and ignored by the adapter.
233       *
234       * @return The fixed search terms, or <jk>null</jk> if none.
235       */
236      public List<Fixed> getFixed() {
237         return fixed;
238      }
239
240      /**
241       * Sets the DataTables 2.x fixed search terms.
242       *
243       * @param v The value.
244       * @return This object.
245       */
246      public Search setFixed(List<Fixed> v) {
247         this.fixed = v;
248         return this;
249      }
250
251      /**
252       * Returns the raw search term.  The term is a {@code $}-language expression.
253       *
254       * @return The search term, or <jk>null</jk> if none.
255       */
256      public String getValue() {
257         return value;
258      }
259
260      /**
261       * Sets the raw search term.
262       *
263       * @param v The value.
264       * @return This object.
265       */
266      public Search setValue(String v) {
267         this.value = v;
268         return this;
269      }
270
271      /**
272       * Returns the DataTables regex flag.  Carried for wire fidelity; the adapter does not use it, since regex
273       * matching is expressed inline with the {@code $regex(...)} operator in {@link #getValue() value}.
274       *
275       * @return The regex flag.
276       */
277      public boolean isRegex() {
278         return regex;
279      }
280
281      /**
282       * Sets the DataTables regex flag.
283       *
284       * @param v The value.
285       * @return This object.
286       */
287      public Search setRegex(boolean v) {
288         this.regex = v;
289         return this;
290      }
291   }
292
293   /**
294    * A DataTables 2.x fixed search term ({@code search.fixed[i]}): a named search applied through the DataTables API
295    * ({@code search.fixed(name, term)}) rather than typed by the user.
296    */
297   public static class Fixed {
298      private String name;
299      private String term;
300
301      /**
302       * Returns the fixed search's name.
303       *
304       * @return The name, or <jk>null</jk> if none.
305       */
306      public String getName() {
307         return name;
308      }
309
310      /**
311       * Sets the fixed search's name.
312       *
313       * @param v The value.
314       * @return This object.
315       */
316      public Fixed setName(String v) {
317         this.name = v;
318         return this;
319      }
320
321      /**
322       * Returns the fixed search's term, as DataTables stringified it (a function or regex arrives as its source).
323       *
324       * @return The term, or <jk>null</jk> if none.
325       */
326      public String getTerm() {
327         return term;
328      }
329
330      /**
331       * Sets the fixed search's term.
332       *
333       * @param v The value.
334       * @return This object.
335       */
336      public Fixed setTerm(String v) {
337         this.term = v;
338         return this;
339      }
340   }
341
342   /**
343    * A DataTables column descriptor ({@code columns[i]}).
344    */
345   public static class Column {
346      private String data;
347      private String name;
348      private boolean searchable = true;
349      private boolean orderable = true;
350      private Search search;
351
352      /**
353       * Returns the column's data source (the row-data key / bean-property name).
354       *
355       * @return The data source, or <jk>null</jk> if none.
356       */
357      @BeanIgnore
358      public String getData() {
359         return data;
360      }
361
362      /**
363       * Bean-property view of {@link #getData() data} typed as {@link Object}, so the property type matches
364       * {@link #setData(Object)} and the parser accepts a bare JSON number as well as a string.
365       *
366       * @return The data source, or <jk>null</jk> if none.
367       */
368      @BeanProp("data")
369      public Object getDataProperty() {
370         return getData();
371      }
372
373      /**
374       * Sets the column's data source.
375       *
376       * <p>
377       * DataTables sends {@code data} as a string (object source) <em>or</em> a bare JSON number (array source), so
378       * this accepts any value and normalizes it to its string form; {@link #getData()} always returns a string.
379       *
380       * @param v The value (a string or a number).
381       * @return This object.
382       */
383      @BeanProp("data")
384      public Column setData(Object v) {
385         this.data = v == null ? null : String.valueOf(v);
386         return this;
387      }
388
389      /**
390       * Returns the column's name (a fallback key when {@link #getData() data} is absent).
391       *
392       * @return The name, or <jk>null</jk> if none.
393       */
394      public String getName() {
395         return name;
396      }
397
398      /**
399       * Sets the column's name.
400       *
401       * @param v The value.
402       * @return This object.
403       */
404      public Column setName(String v) {
405         this.name = v;
406         return this;
407      }
408
409      /**
410       * Returns whether this column may be searched (default <jk>true</jk>).
411       *
412       * @return <jk>true</jk> if searchable.
413       */
414      public boolean isSearchable() {
415         return searchable;
416      }
417
418      /**
419       * Sets whether this column may be searched.
420       *
421       * @param v The value.
422       * @return This object.
423       */
424      public Column setSearchable(boolean v) {
425         this.searchable = v;
426         return this;
427      }
428
429      /**
430       * Returns whether this column may be ordered (default <jk>true</jk>).
431       *
432       * @return <jk>true</jk> if orderable.
433       */
434      public boolean isOrderable() {
435         return orderable;
436      }
437
438      /**
439       * Sets whether this column may be ordered.
440       *
441       * @param v The value.
442       * @return This object.
443       */
444      public Column setOrderable(boolean v) {
445         this.orderable = v;
446         return this;
447      }
448
449      /**
450       * Returns this column's search descriptor.
451       *
452       * @return The column search, or <jk>null</jk> if none.
453       */
454      public Search getSearch() {
455         return search;
456      }
457
458      /**
459       * Sets this column's search descriptor.
460       *
461       * @param v The value.
462       * @return This object.
463       */
464      public Column setSearch(Search v) {
465         this.search = v;
466         return this;
467      }
468   }
469
470   /**
471    * A DataTables ordering descriptor ({@code order[i]}): the index of the ordered column plus its direction.
472    */
473   public static class Order {
474      private int column = -1;
475      private String dir;
476      private String name;
477
478      /**
479       * Returns the index (into {@link DataTablesRequest#getColumns() columns}) of the column to order by.
480       *
481       * @return The column index, or {@code -1} if unset.
482       */
483      public int getColumn() {
484         return column;
485      }
486
487      /**
488       * Sets the index of the column to order by.
489       *
490       * @param v The value.
491       * @return This object.
492       */
493      public Order setColumn(int v) {
494         this.column = v;
495         return this;
496      }
497
498      /**
499       * Returns the sort direction ({@code "asc"} or {@code "desc"}).
500       *
501       * @return The direction, or <jk>null</jk> if none.
502       */
503      public String getDir() {
504         return dir;
505      }
506
507      /**
508       * Sets the sort direction.
509       *
510       * @param v The value.
511       * @return This object.
512       */
513      public Order setDir(String v) {
514         this.dir = v;
515         return this;
516      }
517
518      /**
519       * Returns the ordered column's {@code name} (DataTables 2.x echoes {@code columns[column].name} here).  Carried
520       * for wire fidelity; the adapter resolves the column through {@link #getColumn() column}.
521       *
522       * @return The name, or <jk>null</jk> if none.
523       */
524      public String getName() {
525         return name;
526      }
527
528      /**
529       * Sets the ordered column's name.
530       *
531       * @param v The value.
532       * @return This object.
533       */
534      public Order setName(String v) {
535         this.name = v;
536         return this;
537      }
538   }
539}