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.freemarker; 018 019import java.util.*; 020 021import org.apache.juneau.console.*; 022 023/** 024 * A rail section of checkboxes over the distinct values of one dictionary column of a data source. 025 * 026 * <p> 027 * Publishes {@code {schemaVersion:1, viewId, columns:{<dim>: [selected...]}, options:{deselected:[...]}}}, omitting 028 * {@code columns} and {@code options} when every value is selected. The {@code deselected} list names the values the 029 * user has unticked (capped at {@link #max(int) max}), so a reload can list them again and tick them back. The array 030 * value is a data-source extension: datatables do not apply it. 031 * </p> 032 * 033 * <p> 034 * Unticking every value publishes an empty list ({@code columns:{<dim>: []}}). The data sources map an empty list to 035 * no restriction on the dimension, so "nothing ticked" shows every row, the same as "everything ticked". The section 036 * shows a "Select all" button while a filter is in force. 037 * </p> 038 * 039 * <h5 class='section'>Example:</h5> 040 * <p class='bjava'> 041 * ChoiceSection.<jsm>fromDim</jsm>(<js>"orders"</js>, <js>"reason"</js>).label(<js>"Reason"</js>).max(50) 042 * .publishes(Topics.<jsm>filter</jsm>(<js>"reasons"</js>)); 043 * </p> 044 * 045 * @since 10.0.0 046 */ 047@SuppressWarnings({ 048 "java:S1192" // Repeated literals are contract wire keys; named constants would obscure the contract shape. 049}) 050public final class ChoiceSection extends Section { 051 052 private final String source; 053 private final String dim; 054 private int max = 50; 055 056 private ChoiceSection(String source, String dim) { 057 this.source = source; 058 this.dim = dim; 059 } 060 061 /** 062 * Creates a choice section. 063 * 064 * @param source The data source id (its topic is {@code data:<source>}). 065 * @param dim The dictionary column whose values are listed. 066 * @return A new section. 067 */ 068 public static ChoiceSection fromDim(String source, String dim) { 069 return new ChoiceSection(source, dim); 070 } 071 072 /** 073 * Caps the values shown before "Show all". 074 * 075 * @param value The cap, at least 1. Defaults to 50. 076 * @return This object. 077 */ 078 public ChoiceSection max(int value) { 079 max = value; 080 return this; 081 } 082 083 @Override /* Section */ 084 public ChoiceSection publishes(String topic) { 085 super.publishes(topic); 086 return this; 087 } 088 089 @Override /* Section */ 090 public ChoiceSection label(String label) { 091 super.label(label); 092 return this; 093 } 094 095 @Override String kind() { return "choice"; } 096 097 /** @return The source id. */ 098 String source() { 099 return source; 100 } 101 102 @Override void check(String dash) { 103 super.check(dash); 104 if (! Topics.isKey(source)) 105 throw e77(dash, "choice section '" + id() + "' source '" + source + "' is not a valid topic key"); 106 if (! Topics.isKey(dim)) 107 throw e77(dash, "choice section '" + id() + "' dim '" + dim + "' is not a valid topic key"); 108 if (dim.equals("__proto__") || dim.equals("constructor") || dim.equals("prototype")) 109 throw e77(dash, "choice section '" + id() + "' dim '" + dim + "' is a reserved name"); 110 if (max < 1) 111 throw e77(dash, "choice section '" + id() + "' max must be at least 1"); 112 } 113 114 @Override void body(Map<String,Object> m) { 115 m.put("source", source); 116 m.put("dim", dim); 117 m.put("max", max); 118 } 119}