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.widgets; 018 019import static org.apache.juneau.commons.utils.Shorts.*; 020 021import java.util.*; 022import java.util.function.*; 023 024import org.apache.juneau.commons.svl.*; 025 026/** 027 * A {@link Widget}-marked declaration of named server-side scalar values. 028 * 029 * <p> 030 * This is a thin, typed facade over the existing {@link VarResolver} engine — <b>not</b> a second 031 * templating language. An author declares named scalar value providers (for example {@code failedCount} or 032 * {@code lookbackReadable}); a widget emitter publishes them into a per-render registry and interpolates their 033 * resolved values into widget <i>chrome</i> text (titles / labels) as <js>"$FV{name}"</js> / 034 * <js>"$FV{name,default}"</js> at serve time. 035 * 036 * <p> 037 * <b>Not {@code $W}.</b> {@code $FV} resolves declared scalar values as plain text (serializer-encoded); it is 038 * distinct from {@code $W} ({@code HtmlWidgetVar}), which emits raw widget HTML. {@code $FV} never injects HTML. 039 * 040 * <p> 041 * Providers must return a scalar ({@link String} / {@link Number} / {@link Boolean}) or <jk>null</jk>; non-scalar 042 * results are rejected at resolve time (no {@code List.toString()} join — collection rendering is out of 043 * scope here). Values are session-aware: each provider is a {@link Function} of the per-render 044 * {@link VarResolverSession}. 045 * 046 * <h5 class='section'>Example:</h5> 047 * <p class='bjava'> 048 * ServerValues <jv>sv</jv> = ServerValues.<jsm>create</jsm>() 049 * .value(<js>"failedCount"</js>, <jv>session</jv> -> <jv>metrics</jv>.failed()) 050 * .value(<js>"env"</js>, <jv>session</jv> -> <js>"prod"</js>); 051 * <jc>// Chrome: column title "Failures ($FV{failedCount})" resolves at serve time.</jc> 052 * </p> 053 * 054 * @since 10.0.0 055 */ 056@SuppressWarnings({ 057 "java:S1845" // Fluent-builder setters intentionally mirror field names (Juneau DSL convention). 058}) 059public class ServerValues implements Widget { 060 061 /** The frozen contract version for this widget. */ 062 public static final String CONTRACT_VERSION = "1"; 063 064 /** 065 * Ordered map of interpolation name → value declaration. 066 * 067 * <p> 068 * A {@link LinkedHashMap} for stable {@link #validate()} / emit order. This field is <b>Java-only</b> — 069 * it is never placed on a {@code @BeanType} and never marshals to VIEW_META (lambda providers are not a wire 070 * type). 071 */ 072 public Map<String,ServerValuesValue> values = new LinkedHashMap<>(); 073 074 /** 075 * Creates an empty declaration. 076 * 077 * @return A new {@link ServerValues}. 078 */ 079 public static ServerValues create() { 080 return new ServerValues(); 081 } 082 083 /** 084 * Declares a named scalar value provider. 085 * 086 * @param name The interpolation name (the {@code x} in <js>"$FV{x}"</js>). Must not be <jk>null</jk> or blank. 087 * @param provider The session-aware scalar provider. Must not be <jk>null</jk>. 088 * @return This object. 089 */ 090 public ServerValues value(String name, Function<VarResolverSession,?> provider) { 091 if (ib(name)) 092 throw iaex("ServerValues name must not be null or blank."); 093 if (provider == null) 094 throw iaex("ServerValues provider for ''{0}'' must not be null.", name); 095 if (values.containsKey(name)) 096 throw iaex("ServerValues duplicate name ''{0}''.", name); 097 values.put(name, ServerValuesValue.of(name, provider)); 098 return this; 099 } 100 101 /** 102 * Sets the full ordered map of value declarations. 103 * 104 * @param value The declarations, keyed by interpolation name. Must not be <jk>null</jk>. 105 * @return This object. 106 */ 107 public ServerValues values(Map<String,ServerValuesValue> value) { 108 values = value; 109 return this; 110 } 111 112 @Override /* Widget */ 113 public void validate() { 114 if (values == null) 115 return; 116 for (var e : values.entrySet()) { 117 var name = e.getKey(); 118 if (ib(name)) 119 throw iaex("ServerValues name must not be null or blank."); 120 var v = e.getValue(); 121 if (v == null) 122 throw iaex("ServerValues value for ''{0}'' must not be null.", name); 123 if (v.provider == null) 124 throw iaex("ServerValues provider for ''{0}'' must not be null.", name); 125 } 126 } 127}