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 — 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<Release> 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 — a 232 * strict parser would otherwise reject every DataTables 2.x request — 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}