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.views;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020
021import java.util.*;
022
023import org.apache.juneau.commons.beanquery.*;
024
025/**
026 * A datatable view definition — an ordered set of {@link Column} definitions carrying each column's search
027 * configuration.  This is the bean the {@code VIEW_META} search block is serialized from.
028 *
029 * <p>
030 * A view may also carry a table-level {@link SearchOperatorSet}: it is inherited by every column that
031 * does not supply its own set (see {@link Column#effectiveOperators(SearchOperatorSet)} and
032 * {@link Column#searchMeta(SearchOperatorSet)}).  A column that supplies its own set entirely replaces the
033 * table-level set for that column.
034 *
035 * <h5 class='section'>Mutability</h5>
036 * <p>
037 * Deliberately mutable, like {@link Column} and {@link RegionDef}: a {@link ViewDef} is a server-side authoring
038 * bean built up once via fluent setters and {@link #column(Column)} calls while constructing a view,
039 * not an immutable value object.  Callers must not mutate one concurrently from more than one
040 * thread, and must not reuse one across requests unless every request treats it as read-only after construction.
041 *
042 * <h5 class='section'>Example:</h5>
043 * <p class='bjava'>
044 *    <jc>// A view with a table-level $eq-only default, and one column ("status") that replaces it with $in-only.</jc>
045 *    ViewDef <jv>view</jv> = ViewDef.<jsm>create</jsm>(<js>"releases"</js>)
046 *       .operators(SearchOperatorSet.<jsm>of</jsm>(SearchOperatorSet.<jsm>standard</jsm>().get(<js>"$eq"</js>)))
047 *       .column(Column.<jsm>create</jsm>(<js>"name"</js>).label(<js>"Name"</js>).searchType(SearchType.<jsf>TEXT</jsf>))
048 *       .column(Column.<jsm>create</jsm>(<js>"status"</js>).label(<js>"Status"</js>).searchType(SearchType.<jsf>ID</jsf>)
049 *          .operators(SearchOperatorSet.<jsm>of</jsm>(SearchOperatorSet.<jsm>standard</jsm>().get(<js>"$in"</js>))));
050 * </p>
051 *
052 * <h5 class='section'>See Also:</h5>
053 * <ul>
054 *    <li class='jc'>{@link Column}
055 * </ul>
056 *
057 * @since 10.0.0
058 */
059public final class ViewDef {
060
061   private final String id;
062   private final Map<String,Column> columns = new LinkedHashMap<>();
063   private SearchOperatorSet operators;
064
065   private ViewDef(String id) {
066      this.id = id;
067   }
068
069   /**
070    * Starts a new view definition.
071    *
072    * @param id The view id (matches the datatable's id on the page).  Must not be <jk>null</jk> or blank.
073    * @return A new, empty view definition.
074    * @throws IllegalArgumentException If {@code id} is <jk>null</jk> or blank.
075    */
076   public static ViewDef create(String id) {
077      req(inb(id), "ViewDef id must not be null or blank.");
078      return new ViewDef(id);
079   }
080
081   /**
082    * Adds a column definition, in order.
083    *
084    * @param value The column.  Must not be <jk>null</jk>.
085    * @return This object.
086    * @throws IllegalArgumentException If {@code value} is <jk>null</jk>.
087    */
088   public ViewDef column(Column value) {
089      req(value != null, "ViewDef '%s' column must not be null.", id);
090      columns.put(value.name(), value);
091      return this;
092   }
093
094   /**
095    * Sets the table-level {@link SearchOperatorSet} &mdash; inherited by every column that does not
096    * supply its own set.
097    *
098    * @param value The operator set.  Can be <jk>null</jk> to clear it (columns without their own set then fall
099    *    back to {@link SearchOperatorSet#standard()}).
100    * @return This object.
101    */
102   public ViewDef operators(SearchOperatorSet value) {
103      operators = value;
104      return this;
105   }
106
107   /** @return The view id. */
108   public String id() {
109      return id;
110   }
111
112   /**
113    * The column definitions, in insertion order.
114    *
115    * @return An unmodifiable list of the columns.
116    */
117   public List<Column> columns() {
118      return List.copyOf(columns.values());
119   }
120
121   /**
122    * Looks up a column by name.
123    *
124    * @param name The column name.  Can be <jk>null</jk>.
125    * @return The column, or <jk>null</jk> if no column has that name.
126    */
127   public Column findColumn(String name) {
128      return name == null ? null : columns.get(name);
129   }
130
131   /**
132    * This view's table-level operator set, or <jk>null</jk> if absent (columns without their own set fall back to
133    * {@link SearchOperatorSet#standard()}).
134    *
135    * @return The operator set, or <jk>null</jk>.
136    */
137   public SearchOperatorSet operators() {
138      return operators;
139   }
140}