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.*; 020import java.util.regex.*; 021 022/** 023 * A rail section of quick time-range presets, with an optional custom from/to. 024 * 025 * <p> 026 * Publishes {@code {schemaVersion:1, viewId, options:{range}, start, end}} on its {@code filter:} topic, where 027 * {@code range} is the preset or {@code "custom"} and {@code start}/{@code end} are ISO-8601 UTC instants. 028 * {@code Nd} and {@code Nh} end at now floored to the minute; {@code ytd} starts at January 1, 00:00 UTC. 029 * </p> 030 * 031 * <h5 class='section'>Example:</h5> 032 * <p class='bjava'> 033 * RangeSection.<jsm>quick</jsm>(<js>"7d"</js>, <js>"30d"</js>, <js>"90d"</js>, <js>"ytd"</js>).custom(<jk>true</jk>).defaultPreset(<js>"30d"</js>) 034 * .publishes(Topics.<jsm>filter</jsm>(<js>"range"</js>)); 035 * </p> 036 * 037 * @since 10.0.0 038 */ 039@SuppressWarnings({ 040 "java:S1192" // Repeated literals are contract wire keys; named constants would obscure the contract shape. 041}) 042public final class RangeSection extends Section { 043 044 private static final Pattern PRESET = Pattern.compile("^([1-9]\\d{0,3}[dh]|ytd)$"); 045 046 private final List<String> presets; 047 private boolean custom; 048 private String defaultPreset; 049 050 private RangeSection(String...presets) { 051 this.presets = Arrays.asList(presets); // A null preset is reported by check(), not an NPE here. 052 } 053 054 /** 055 * Creates a range section. 056 * 057 * @param presets The quick presets, each {@code Nd}, {@code Nh} or {@code ytd}. 058 * @return A new section. 059 */ 060 public static RangeSection quick(String...presets) { 061 return new RangeSection(presets); 062 } 063 064 /** 065 * Adds the custom from/to inputs (read as UTC). 066 * 067 * @param value <jk>true</jk> to show them. 068 * @return This object. 069 */ 070 public RangeSection custom(boolean value) { 071 custom = value; 072 return this; 073 } 074 075 /** 076 * Sets the preset used when nothing is restored. 077 * 078 * @param value One of the presets. Defaults to the first. 079 * @return This object. 080 */ 081 public RangeSection defaultPreset(String value) { 082 defaultPreset = value; 083 return this; 084 } 085 086 @Override /* Section */ 087 public RangeSection publishes(String topic) { 088 super.publishes(topic); 089 return this; 090 } 091 092 @Override /* Section */ 093 public RangeSection label(String label) { 094 super.label(label); 095 return this; 096 } 097 098 @Override String kind() { return "range"; } 099 100 @Override void check(String dash) { 101 super.check(dash); 102 if (presets.isEmpty()) 103 throw e77(dash, "range section '" + id() + "' needs at least one preset"); 104 var seen = new HashSet<String>(); 105 for (var p : presets) { 106 if (p == null || ! PRESET.matcher(p).matches()) 107 throw e77(dash, "range section '" + id() + "' preset '" + p + "' is not Nd, Nh or ytd"); 108 if (! seen.add(p)) 109 throw e77(dash, "range section '" + id() + "' lists preset '" + p + "' twice"); 110 } 111 if (defaultPreset != null && ! presets.contains(defaultPreset)) 112 throw e77(dash, "range section '" + id() + "' default preset '" + defaultPreset + "' is not one of its presets"); 113 } 114 115 @Override void body(Map<String,Object> m) { 116 m.put("presets", presets); 117 m.put("custom", custom); 118 m.put("defaultPreset", defaultPreset != null ? defaultPreset : presets.get(0)); 119 } 120}