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 org.apache.juneau.commons.bean.*; 022import org.apache.juneau.console.widgets.Op; 023 024/** 025 * A declarative disable-with-reason rule gating a {@link RowAction} on the row's own state, in the 026 * {@code VIEW_META} wire contract. 027 * 028 * <p> 029 * Mirrors {@code org.apache.juneau.console.widgets.ActionRef.EnabledRule} on a detail-panel 030 * action bar, but rides the wire as a plain data rule the same way a row-class 031 * rule does — a table row is polled/client-rendered rather than server-painted, so this type follows 032 * that wire shape, not {@code ActionRef.EnabledRule}'s Java-only one. Serializes to 033 * <c>{"field":..., "op":..., "value"?:..., "reason":...}</c>, with {@link #op} carrying {@link Op#wire()}'s 034 * lowercase token (a {@link String}), not the Java enum. 035 * 036 * <p> 037 * The {@link Op#EQ eq}/{@link Op#NE ne} operators <b>require</b> a {@link #value}; the {@link Op#PRESENT 038 * present}/{@link Op#ABSENT absent} operators test only whether {@code row[field]} is non-null/non-empty and 039 * therefore <b>omit</b> {@code value} from the wire. 040 * 041 * <h5 class='section'>Presentation only, never the authorization gate:</h5> 042 * <p> 043 * A row whose field does not satisfy this rule renders its {@link RowAction} present but disabled, carrying 044 * {@link #reason} as a tooltip and {@code aria-describedby} text. The endpoint behind the action must still 045 * perform its own real check and refuse on its own authority; this rule is never a substitute for that check. 046 * 047 * <h5 class='section'>See Also:</h5> 048 * <ul> 049 * <li class='jc'>{@link RowAction} 050 * </ul> 051 * 052 * @since 10.0.0 053 */ 054@BeanType(properties="field,op,value,reason") 055public class RowActionEnabledRule { 056 057 /** The row field this rule tests. */ 058 public String field; 059 060 /** The wire token of the comparison operator (see {@link Op#wire()}). */ 061 public String op; 062 063 /** The comparison value for {@code eq}/{@code ne}; omitted for {@code present}/{@code absent}. */ 064 public Object value; 065 066 /** Why the action is unavailable when this rule does not match. Required — must not be blank. */ 067 public String reason; 068 069 /** 070 * Creates a value-based rule for the {@link Op#EQ eq}/{@link Op#NE ne} operators. 071 * 072 * @param field The row field to test. Must not be <jk>null</jk> or blank. 073 * @param op The operator. Must be {@link Op#EQ} or {@link Op#NE}. 074 * @param value The comparison value. Must not be <jk>null</jk>. 075 * @param reason Why the action is unavailable when this rule does not match. Must not be <jk>null</jk> or 076 * blank. 077 * @return A new {@link RowActionEnabledRule}. 078 * @throws IllegalArgumentException If {@code field}/{@code reason} is <jk>null</jk> or blank, {@code op} is 079 * not value-based, or {@code value} is <jk>null</jk>. 080 */ 081 public static RowActionEnabledRule of(String field, Op op, Object value, String reason) { 082 if (ib(field)) 083 throw iaex("RowActionEnabledRule field must not be null or blank."); 084 if (op != null && op.isCollectionOp()) 085 throw iaex("RowActionEnabledRule op '%s' is a visibility-rule operator; use eq or ne.", op.wire()); 086 if (op == null || ! op.requiresValue()) 087 throw iaex("RowActionEnabledRule op '%s' does not take a value; use the (field, op, reason) form.", 088 op == null ? "null" : op.wire()); 089 if (value == null) 090 throw iaex("RowActionEnabledRule op '%s' requires a non-null value.", op.wire()); 091 if (ib(reason)) 092 throw iaex("RowActionEnabledRule reason must not be null or blank."); 093 var r = new RowActionEnabledRule(); 094 r.field = field; 095 r.op = op.wire(); 096 r.value = value; 097 r.reason = reason; 098 return r; 099 } 100 101 /** 102 * Creates a presence-based rule for the {@link Op#PRESENT present}/{@link Op#ABSENT absent} operators. 103 * 104 * @param field The row field to test. Must not be <jk>null</jk> or blank. 105 * @param op The operator. Must be {@link Op#PRESENT} or {@link Op#ABSENT}. 106 * @param reason Why the action is unavailable when this rule does not match. Must not be <jk>null</jk> or 107 * blank. 108 * @return A new {@link RowActionEnabledRule} with no {@code value}. 109 * @throws IllegalArgumentException If {@code field}/{@code reason} is <jk>null</jk> or blank, or {@code op} 110 * is value-based. 111 */ 112 public static RowActionEnabledRule of(String field, Op op, String reason) { 113 if (ib(field)) 114 throw iaex("RowActionEnabledRule field must not be null or blank."); 115 if (op == null || op.requiresValue()) 116 throw iaex("RowActionEnabledRule op '%s' requires a value; use the (field, op, value, reason) form.", 117 op == null ? "null" : op.wire()); 118 if (ib(reason)) 119 throw iaex("RowActionEnabledRule reason must not be null or blank."); 120 var r = new RowActionEnabledRule(); 121 r.field = field; 122 r.op = op.wire(); 123 r.reason = reason; 124 return r; 125 } 126}