Damien Martin-Guillerez | ebb0039 | 2015-09-29 11:55:59 +0000 | [diff] [blame] | 1 | // Copyright 2015 The Bazel Authors. All rights reserved. |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 | // you may not use this file except in compliance with the License. |
| 5 | // You may obtain a copy of the License at |
| 6 | // |
| 7 | // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 | // |
| 9 | // Unless required by applicable law or agreed to in writing, software |
| 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 | // See the License for the specific language governing permissions and |
| 13 | // limitations under the License. |
ccalvarin | f39dc6f | 2017-06-09 11:51:45 -0400 | [diff] [blame] | 14 | package com.google.devtools.common.options; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 15 | |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 16 | import static java.util.stream.Collectors.joining; |
| 17 | |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 18 | import com.google.common.base.Verify; |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 19 | import com.google.common.collect.ArrayListMultimap; |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 20 | import com.google.common.collect.ImmutableList; |
Janak Ramakrishnan | e9cd0f3 | 2016-04-29 22:46:16 +0000 | [diff] [blame] | 21 | import com.google.common.collect.ImmutableSet; |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 22 | import com.google.common.collect.Multimap; |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 23 | import com.google.common.flogger.GoogleLogger; |
| 24 | import com.google.common.flogger.LazyArgs; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 25 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.AllowValues; |
| 26 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.DisallowValues; |
| 27 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.FlagPolicy; |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 28 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.FlagPolicy.OperationCase; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 29 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.InvocationPolicy; |
| 30 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue; |
ajurkowski | ecab297 | 2021-06-17 14:26:34 -0700 | [diff] [blame] | 31 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.SetValue.Behavior; |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 32 | import com.google.devtools.build.lib.runtime.proto.InvocationPolicyOuterClass.UseDefault; |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 33 | import com.google.devtools.common.options.OptionPriority.PriorityCategory; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 34 | import com.google.devtools.common.options.OptionsParser.OptionDescription; |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 35 | import java.util.ArrayList; |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 36 | import java.util.Collections; |
| 37 | import java.util.HashMap; |
Jonathan Bluett-Duncan | 0df3ddbd | 2017-08-09 11:13:54 +0200 | [diff] [blame] | 38 | import java.util.HashSet; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 39 | import java.util.List; |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 40 | import java.util.Map; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 41 | import java.util.Set; |
Janak Ramakrishnan | b40cbaa | 2016-11-18 00:20:45 +0000 | [diff] [blame] | 42 | import java.util.logging.Level; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 43 | import javax.annotation.Nullable; |
| 44 | |
| 45 | /** |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 46 | * Enforces the {@link FlagPolicy}s (from an {@link InvocationPolicy} proto) on an {@link |
| 47 | * OptionsParser} by validating and changing the flag values in the given {@link OptionsParser}. |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 48 | * |
| 49 | * <p>"Flag" and "Option" are used interchangeably in this file. |
| 50 | */ |
| 51 | public final class InvocationPolicyEnforcer { |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 52 | private static final GoogleLogger logger = GoogleLogger.forEnclosingClass(); |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 53 | |
ccalvarin | 5fe8e66 | 2017-09-14 15:56:43 +0200 | [diff] [blame] | 54 | private static final String INVOCATION_POLICY_SOURCE = "Invocation policy"; |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 55 | @Nullable private final InvocationPolicy invocationPolicy; |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 56 | private final Level loglevel; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 57 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 58 | /** |
| 59 | * Creates an InvocationPolicyEnforcer that enforces the given policy. |
| 60 | * |
| 61 | * @param invocationPolicy the policy to enforce. A null policy means this enforcer will do |
| 62 | * nothing in calls to enforce(). |
| 63 | */ |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 64 | public InvocationPolicyEnforcer(@Nullable InvocationPolicy invocationPolicy) { |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 65 | this(invocationPolicy, Level.FINE); |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * Creates an InvocationPolicyEnforcer that enforces the given policy. |
| 70 | * |
| 71 | * @param invocationPolicy the policy to enforce. A null policy means this enforcer will do |
| 72 | * nothing in calls to enforce(). |
| 73 | * @param loglevel the level at which to log informational statements. Warnings and errors will |
| 74 | * still be logged at the appropriate level. |
| 75 | */ |
| 76 | public InvocationPolicyEnforcer(@Nullable InvocationPolicy invocationPolicy, Level loglevel) { |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 77 | this.invocationPolicy = invocationPolicy; |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 78 | this.loglevel = loglevel; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 79 | } |
| 80 | |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 81 | private static final class FlagPolicyWithContext { |
| 82 | private final FlagPolicy policy; |
| 83 | private final OptionDescription description; |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 84 | private final OptionInstanceOrigin origin; |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 85 | |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 86 | public FlagPolicyWithContext( |
| 87 | FlagPolicy policy, OptionDescription description, OptionInstanceOrigin origin) { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 88 | this.policy = policy; |
| 89 | this.description = description; |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 90 | this.origin = origin; |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 91 | } |
| 92 | } |
| 93 | |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 94 | @Nullable |
Ulf Adams | 015aad9 | 2016-07-13 16:49:40 +0000 | [diff] [blame] | 95 | public InvocationPolicy getInvocationPolicy() { |
| 96 | return invocationPolicy; |
| 97 | } |
| 98 | |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 99 | /** |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 100 | * Applies this OptionsPolicyEnforcer's policy to the given OptionsParser for all blaze commands. |
| 101 | * |
| 102 | * @param parser The OptionsParser to enforce policy on. |
| 103 | * @throws OptionsParsingException if any flag policy is invalid. |
| 104 | */ |
| 105 | public void enforce(OptionsParser parser) throws OptionsParsingException { |
Janak Ramakrishnan | e9cd0f3 | 2016-04-29 22:46:16 +0000 | [diff] [blame] | 106 | enforce(parser, null); |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | /** |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 110 | * Applies this OptionsPolicyEnforcer's policy to the given OptionsParser. |
| 111 | * |
| 112 | * @param parser The OptionsParser to enforce policy on. |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 113 | * @param command The current blaze command, for flag policies that apply to only specific |
Janak Ramakrishnan | e9cd0f3 | 2016-04-29 22:46:16 +0000 | [diff] [blame] | 114 | * commands. Such policies will be enforced only if they contain this command or a command |
| 115 | * they inherit from |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 116 | * @throws OptionsParsingException if any flag policy is invalid. |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 117 | */ |
Janak Ramakrishnan | e9cd0f3 | 2016-04-29 22:46:16 +0000 | [diff] [blame] | 118 | public void enforce(OptionsParser parser, @Nullable String command) |
| 119 | throws OptionsParsingException { |
Luis Fernando Pino Duque | 5816b3b | 2016-04-11 15:33:26 +0000 | [diff] [blame] | 120 | if (invocationPolicy == null || invocationPolicy.getFlagPoliciesCount() == 0) { |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 121 | return; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 122 | } |
| 123 | |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 124 | // The effective policy returned is expanded, filtered for applicable commands, and cleaned of |
| 125 | // redundancies and conflicts. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 126 | List<FlagPolicyWithContext> effectivePolicies = |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 127 | getEffectivePolicies(invocationPolicy, parser, command, loglevel); |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 128 | |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 129 | for (FlagPolicyWithContext flagPolicy : effectivePolicies) { |
| 130 | String flagName = flagPolicy.policy.getFlagName(); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 131 | |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 132 | OptionValueDescription valueDescription; |
| 133 | try { |
| 134 | valueDescription = parser.getOptionValueDescription(flagName); |
| 135 | } catch (IllegalArgumentException e) { |
| 136 | // This flag doesn't exist. We are deliberately lenient if the flag policy has a flag |
| 137 | // we don't know about. This is for better future proofing so that as new flags are added, |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 138 | // new policies can use the new flags without worrying about older versions of Bazel. |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 139 | logger.at(loglevel).log( |
| 140 | "Flag '%s' specified by invocation policy does not exist", flagName); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 141 | continue; |
| 142 | } |
| 143 | |
ccalvarin | 5fe8e66 | 2017-09-14 15:56:43 +0200 | [diff] [blame] | 144 | // getOptionDescription() will return null if the option does not exist, however |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 145 | // getOptionValueDescription() above would have thrown an IllegalArgumentException if that |
| 146 | // were the case. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 147 | Verify.verifyNotNull(flagPolicy.description); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 148 | |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 149 | switch (flagPolicy.policy.getOperationCase()) { |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 150 | case SET_VALUE: |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 151 | applySetValueOperation(parser, flagPolicy, valueDescription, loglevel); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 152 | break; |
| 153 | |
| 154 | case USE_DEFAULT: |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 155 | applyUseDefaultOperation( |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 156 | parser, "UseDefault", flagPolicy.description.getOptionDefinition(), loglevel); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 157 | break; |
| 158 | |
| 159 | case ALLOW_VALUES: |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 160 | AllowValues allowValues = flagPolicy.policy.getAllowValues(); |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 161 | FilterValueOperation.AllowValueOperation allowValueOperation = |
| 162 | new FilterValueOperation.AllowValueOperation(loglevel); |
| 163 | allowValueOperation.apply( |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 164 | parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 165 | flagPolicy.origin, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 166 | allowValues.getAllowedValuesList(), |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 167 | allowValues.hasNewValue() ? allowValues.getNewValue() : null, |
| 168 | allowValues.hasUseDefault(), |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 169 | valueDescription, |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 170 | flagPolicy.description); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 171 | break; |
| 172 | |
| 173 | case DISALLOW_VALUES: |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 174 | DisallowValues disallowValues = flagPolicy.policy.getDisallowValues(); |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 175 | FilterValueOperation.DisallowValueOperation disallowValueOperation = |
| 176 | new FilterValueOperation.DisallowValueOperation(loglevel); |
| 177 | disallowValueOperation.apply( |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 178 | parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 179 | flagPolicy.origin, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 180 | disallowValues.getDisallowedValuesList(), |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 181 | disallowValues.hasNewValue() ? disallowValues.getNewValue() : null, |
| 182 | disallowValues.hasUseDefault(), |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 183 | valueDescription, |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 184 | flagPolicy.description); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 185 | break; |
| 186 | |
| 187 | case OPERATION_NOT_SET: |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 188 | throw new PolicyOperationNotSetException(flagName); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 189 | |
| 190 | default: |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 191 | logger.atWarning().log( |
| 192 | "Unknown operation '%s' from invocation policy for flag '%s'", |
| 193 | flagPolicy.policy.getOperationCase(), flagName); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 194 | break; |
| 195 | } |
| 196 | } |
| 197 | } |
| 198 | |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 199 | private static class PolicyOperationNotSetException extends OptionsParsingException { |
| 200 | PolicyOperationNotSetException(String flagName) { |
| 201 | super(String.format("Flag policy for flag '%s' does not " + "have an operation", flagName)); |
| 202 | } |
| 203 | } |
| 204 | |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 205 | private static boolean policyApplies(FlagPolicy policy, ImmutableSet<String> applicableCommands) { |
| 206 | // Skip the flag policy if it doesn't apply to this command. If the commands list is empty, |
| 207 | // then the policy applies to all commands. |
| 208 | if (policy.getCommandsList().isEmpty() || applicableCommands.isEmpty()) { |
| 209 | return true; |
| 210 | } |
| 211 | |
| 212 | return !Collections.disjoint(policy.getCommandsList(), applicableCommands); |
| 213 | } |
| 214 | |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 215 | /** Returns the expanded and filtered policy that would be enforced for the given command. */ |
| 216 | public static InvocationPolicy getEffectiveInvocationPolicy( |
| 217 | InvocationPolicy invocationPolicy, OptionsParser parser, String command, Level loglevel) |
| 218 | throws OptionsParsingException { |
| 219 | ImmutableList<FlagPolicyWithContext> effectivePolicies = |
| 220 | getEffectivePolicies(invocationPolicy, parser, command, loglevel); |
| 221 | |
| 222 | InvocationPolicy.Builder builder = InvocationPolicy.newBuilder(); |
| 223 | for (FlagPolicyWithContext policyWithContext : effectivePolicies) { |
| 224 | builder.addFlagPolicies(policyWithContext.policy); |
| 225 | } |
| 226 | return builder.build(); |
| 227 | } |
| 228 | |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 229 | /** |
| 230 | * Takes the provided policy and processes it to the form that can be used on the user options. |
| 231 | * |
| 232 | * <p>Expands any policies on expansion flags. |
| 233 | */ |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 234 | private static ImmutableList<FlagPolicyWithContext> getEffectivePolicies( |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 235 | InvocationPolicy invocationPolicy, OptionsParser parser, String command, Level loglevel) |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 236 | throws OptionsParsingException { |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 237 | if (invocationPolicy == null) { |
| 238 | return ImmutableList.of(); |
| 239 | } |
| 240 | |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 241 | ImmutableSet<String> commandAndParentCommands = |
| 242 | command == null |
Jonathan Bluett-Duncan | 0df3ddbd | 2017-08-09 11:13:54 +0200 | [diff] [blame] | 243 | ? ImmutableSet.of() |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 244 | : CommandNameCache.CommandNameCacheInstance.INSTANCE.get(command); |
| 245 | |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 246 | // Expand all policies to transfer policies on expansion flags to policies on the child flags. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 247 | List<FlagPolicyWithContext> expandedPolicies = new ArrayList<>(); |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 248 | OptionPriority nextPriority = |
| 249 | OptionPriority.lowestOptionPriorityAtCategory(PriorityCategory.INVOCATION_POLICY); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 250 | for (FlagPolicy policy : invocationPolicy.getFlagPoliciesList()) { |
ccalvarin | 9c8c775 | 2018-04-17 07:48:38 -0700 | [diff] [blame] | 251 | // Explicitly disallow --config in invocation policy. |
| 252 | if (policy.getFlagName().equals("config")) { |
| 253 | throw new OptionsParsingException( |
| 254 | "Invocation policy is applied after --config expansion, changing config values now " |
| 255 | + "would have no effect and is disallowed to prevent confusion. Please remove the " |
| 256 | + "following policy : " |
| 257 | + policy); |
| 258 | } |
| 259 | |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 260 | // These policies are high-level, before expansion, and so are not the implicitDependents or |
| 261 | // expansions of any other flag, other than in an obtuse sense from --invocation_policy. |
| 262 | OptionPriority currentPriority = nextPriority; |
| 263 | OptionInstanceOrigin origin = |
| 264 | new OptionInstanceOrigin(currentPriority, INVOCATION_POLICY_SOURCE, null, null); |
| 265 | nextPriority = OptionPriority.nextOptionPriority(currentPriority); |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 266 | if (!policyApplies(policy, commandAndParentCommands)) { |
| 267 | // Only keep and expand policies that are applicable to the current command. |
| 268 | continue; |
| 269 | } |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 270 | |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 271 | OptionDescription optionDescription = parser.getOptionDescription(policy.getFlagName()); |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 272 | if (optionDescription == null) { |
| 273 | // InvocationPolicy ignores policy on non-existing flags by design, for version |
| 274 | // compatibility. |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 275 | logger.at(loglevel).log( |
| 276 | "Flag '%s' specified by invocation policy does not exist, and will be ignored", |
| 277 | policy.getFlagName()); |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 278 | continue; |
| 279 | } |
| 280 | FlagPolicyWithContext policyWithContext = |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 281 | new FlagPolicyWithContext(policy, optionDescription, origin); |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 282 | List<FlagPolicyWithContext> policies = expandPolicy(policyWithContext, parser, loglevel); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 283 | expandedPolicies.addAll(policies); |
| 284 | } |
| 285 | |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 286 | // Only keep that last policy for each flag. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 287 | Map<String, FlagPolicyWithContext> effectivePolicy = new HashMap<>(); |
| 288 | for (FlagPolicyWithContext expandedPolicy : expandedPolicies) { |
| 289 | String flagName = expandedPolicy.policy.getFlagName(); |
ccalvarin | efc9b63 | 2017-05-03 19:05:22 +0200 | [diff] [blame] | 290 | effectivePolicy.put(flagName, expandedPolicy); |
| 291 | } |
| 292 | |
Googler | 7c7255e | 2017-06-27 20:05:20 +0200 | [diff] [blame] | 293 | return ImmutableList.copyOf(effectivePolicy.values()); |
| 294 | } |
| 295 | |
| 296 | private static void throwAllowValuesOnExpansionFlagException(String flagName) |
| 297 | throws OptionsParsingException { |
| 298 | throw new OptionsParsingException( |
| 299 | String.format("Allow_Values on expansion flags like %s is not allowed.", flagName)); |
| 300 | } |
| 301 | |
| 302 | private static void throwDisallowValuesOnExpansionFlagException(String flagName) |
| 303 | throws OptionsParsingException { |
| 304 | throw new OptionsParsingException( |
| 305 | String.format("Disallow_Values on expansion flags like %s is not allowed.", flagName)); |
| 306 | } |
| 307 | |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 308 | private static OptionsParsingException throwUndefinedBehaviorException(FlagPolicy policy) |
| 309 | throws OptionsParsingException { |
| 310 | throw new OptionsParsingException( |
| 311 | String.format( |
| 312 | "SetValue operation from invocation policy for has an undefined behavior: %s", policy)); |
| 313 | } |
| 314 | |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 315 | /** |
| 316 | * Expand a single policy. If the policy is not about an expansion flag, this will simply return a |
| 317 | * list with a single element, oneself. If the policy is for an expansion flag, the policy will |
| 318 | * get split into multiple policies applying to each flag the original flag expands to. |
| 319 | * |
| 320 | * <p>None of the flagPolicies returned should be on expansion flags. |
| 321 | */ |
ajurkowski | 6977067 | 2021-06-04 04:43:28 -0700 | [diff] [blame] | 322 | private static ImmutableList<FlagPolicyWithContext> expandPolicy( |
ccalvarin | 06e6874 | 2018-03-01 07:29:45 -0800 | [diff] [blame] | 323 | FlagPolicyWithContext originalPolicy, OptionsParser parser, Level loglevel) |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 324 | throws OptionsParsingException { |
ajurkowski | 6977067 | 2021-06-04 04:43:28 -0700 | [diff] [blame] | 325 | ImmutableList.Builder<FlagPolicyWithContext> expandedPolicies = ImmutableList.builder(); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 326 | |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 327 | boolean isExpansion = originalPolicy.description.isExpansion(); |
| 328 | ImmutableList<ParsedOptionDescription> subflags = |
| 329 | parser.getExpansionValueDescriptions( |
| 330 | originalPolicy.description.getOptionDefinition(), originalPolicy.origin); |
| 331 | |
| 332 | // If we have nothing to expand to, no need to do any further work. |
| 333 | if (subflags.isEmpty()) { |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 334 | return ImmutableList.of(originalPolicy); |
| 335 | } |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 336 | |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 337 | // Log the expansion. This is only really useful for understanding the invocation policy itself. |
| 338 | logger.at(loglevel).log( |
| 339 | "Expanding %s on option %s to its %s: %s.", |
| 340 | originalPolicy.policy.getOperationCase(), |
| 341 | originalPolicy.policy.getFlagName(), |
| 342 | isExpansion ? "expansions" : "implied flags", |
| 343 | LazyArgs.lazy( |
| 344 | () -> |
| 345 | subflags.stream() |
| 346 | .map(f -> "--" + f.getOptionDefinition().getOptionName()) |
| 347 | .collect(joining("; ")))); |
ccalvarin | 36109fc | 2017-04-05 15:18:51 +0000 | [diff] [blame] | 348 | |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 349 | // Repeated flags are special, and could set multiple times in an expansion, with the user |
| 350 | // expecting both values to be valid. Collect these separately. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 351 | Multimap<OptionDescription, ParsedOptionDescription> repeatableSubflagsInSetValues = |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 352 | ArrayListMultimap.create(); |
| 353 | |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 354 | // Create a flag policy for the child that looks like the parent's policy "transferred" to its |
| 355 | // child. Note that this only makes sense for SetValue, when setting an expansion flag, or |
| 356 | // UseDefault, when preventing it from being set. |
ccalvarin | fb153cd | 2017-09-15 19:29:50 +0200 | [diff] [blame] | 357 | for (ParsedOptionDescription currentSubflag : subflags) { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 358 | OptionDescription subflagOptionDescription = |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 359 | parser.getOptionDescription(currentSubflag.getOptionDefinition().getOptionName()); |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 360 | |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 361 | if (currentSubflag.getOptionDefinition().allowsMultiple() |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 362 | && originalPolicy.policy.getOperationCase().equals(OperationCase.SET_VALUE)) { |
| 363 | repeatableSubflagsInSetValues.put(subflagOptionDescription, currentSubflag); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 364 | } else { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 365 | FlagPolicyWithContext subflagAsPolicy = |
| 366 | getSingleValueSubflagAsPolicy( |
| 367 | subflagOptionDescription, currentSubflag, originalPolicy, isExpansion); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 368 | // In case any of the expanded flags are themselves expansions, recurse. |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 369 | expandedPolicies.addAll(expandPolicy(subflagAsPolicy, parser, loglevel)); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 370 | } |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 371 | } |
| 372 | |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 373 | // If there are any repeatable flag SetValues, deal with them together now. |
| 374 | // Note that expansion flags have no value, and so cannot have multiple values either. |
| 375 | // Skipping the recursion above is fine. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 376 | for (OptionDescription repeatableFlag : repeatableSubflagsInSetValues.keySet()) { |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 377 | int numValues = repeatableSubflagsInSetValues.get(repeatableFlag).size(); |
| 378 | ArrayList<String> newValues = new ArrayList<>(numValues); |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 379 | ArrayList<OptionInstanceOrigin> origins = new ArrayList<>(numValues); |
ccalvarin | fb153cd | 2017-09-15 19:29:50 +0200 | [diff] [blame] | 380 | for (ParsedOptionDescription setValue : repeatableSubflagsInSetValues.get(repeatableFlag)) { |
ccalvarin | a8c0c8d | 2017-09-14 16:54:39 +0200 | [diff] [blame] | 381 | newValues.add(setValue.getUnconvertedValue()); |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 382 | origins.add(setValue.getOrigin()); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 383 | } |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 384 | // These options come from expanding a single policy, so they have effectively the same |
| 385 | // priority. They could have come from different expansions or implicit requirements in the |
| 386 | // recursive resolving of the option list, so just pick the first one. Do collapse the source |
| 387 | // strings though, in case there are different sources. |
| 388 | OptionInstanceOrigin arbitraryFirstOptionOrigin = origins.get(0); |
| 389 | OptionInstanceOrigin originOfSubflags = |
| 390 | new OptionInstanceOrigin( |
| 391 | arbitraryFirstOptionOrigin.getPriority(), |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 392 | origins.stream() |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 393 | .map(OptionInstanceOrigin::getSource) |
| 394 | .distinct() |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 395 | .collect(joining(", ")), |
ccalvarin | 34a9fea | 2017-10-17 23:27:19 +0200 | [diff] [blame] | 396 | arbitraryFirstOptionOrigin.getImplicitDependent(), |
| 397 | arbitraryFirstOptionOrigin.getExpandedFrom()); |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 398 | expandedPolicies.add( |
| 399 | getSetValueSubflagAsPolicy(repeatableFlag, newValues, originOfSubflags, originalPolicy)); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 400 | } |
| 401 | |
| 402 | // Don't add the original policy if it was an expansion flag, which have no value, but do add |
| 403 | // it if there was either no expansion or if it was a valued flag with implicit requirements. |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 404 | if (!isExpansion) { |
Googler | 7c7255e | 2017-06-27 20:05:20 +0200 | [diff] [blame] | 405 | expandedPolicies.add(originalPolicy); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 406 | } |
| 407 | |
ajurkowski | 6977067 | 2021-06-04 04:43:28 -0700 | [diff] [blame] | 408 | return expandedPolicies.build(); |
ccalvarin | 706bafe | 2017-03-30 17:34:04 +0000 | [diff] [blame] | 409 | } |
| 410 | |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 411 | /** |
| 412 | * Expand a SetValue flag policy on a repeatable flag. SetValue operations are the only flag |
| 413 | * policies that set the flag, and so interact with repeatable flags, flags that can be set |
| 414 | * multiple times, in subtle ways. |
| 415 | * |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 416 | * @param subflagDesc, the description of the flag the SetValue'd expansion flag expands to. |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 417 | * @param subflagValue, the values that the SetValue'd expansion flag expands to for this flag. |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 418 | * @param originalPolicy, the original policy on the expansion flag. |
| 419 | * @return the flag policy for the subflag given, this will be part of the expanded form of the |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 420 | * SetValue policy on the original flag. |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 421 | */ |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 422 | private static FlagPolicyWithContext getSetValueSubflagAsPolicy( |
| 423 | OptionDescription subflagDesc, |
| 424 | List<String> subflagValue, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 425 | OptionInstanceOrigin subflagOrigin, |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 426 | FlagPolicyWithContext originalPolicy) |
| 427 | throws OptionsParsingException { |
Googler | 199e70c | 2020-10-08 19:57:27 -0700 | [diff] [blame] | 428 | // Some checks. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 429 | OptionDefinition subflag = subflagDesc.getOptionDefinition(); |
| 430 | Verify.verify(originalPolicy.policy.getOperationCase().equals(OperationCase.SET_VALUE)); |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 431 | if (!subflag.allowsMultiple()) { |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 432 | Verify.verify(subflagValue.size() <= 1); |
| 433 | } |
| 434 | |
| 435 | // Flag value from the expansion, overridability from the original policy, unless the flag is |
| 436 | // repeatable, in which case we care about appendability, not overridability. |
| 437 | SetValue.Builder setValueExpansion = SetValue.newBuilder(); |
| 438 | for (String value : subflagValue) { |
| 439 | setValueExpansion.addFlagValue(value); |
| 440 | } |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 441 | |
| 442 | switch (originalPolicy.policy.getSetValue().getBehavior()) { |
| 443 | case UNDEFINED: |
| 444 | throw throwUndefinedBehaviorException(originalPolicy.policy); |
| 445 | case FINAL_VALUE_IGNORE_OVERRIDES: |
| 446 | case APPEND: |
| 447 | setValueExpansion.setBehavior(Behavior.FINAL_VALUE_IGNORE_OVERRIDES); |
| 448 | break; |
| 449 | case ALLOW_OVERRIDES: |
| 450 | setValueExpansion.setBehavior( |
| 451 | subflag.allowsMultiple() ? Behavior.APPEND : Behavior.ALLOW_OVERRIDES); |
| 452 | break; |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 453 | } |
| 454 | |
| 455 | // Commands from the original policy, flag name of the expansion |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 456 | return new FlagPolicyWithContext( |
| 457 | FlagPolicy.newBuilder() |
| 458 | .addAllCommands(originalPolicy.policy.getCommandsList()) |
| 459 | .setFlagName(subflag.getOptionName()) |
| 460 | .setSetValue(setValueExpansion) |
| 461 | .build(), |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 462 | subflagDesc, |
| 463 | subflagOrigin); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 464 | } |
| 465 | |
| 466 | /** |
| 467 | * For an expansion flag in an invocation policy, each flag it expands to must be given a |
| 468 | * corresponding policy. |
| 469 | */ |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 470 | private static FlagPolicyWithContext getSingleValueSubflagAsPolicy( |
| 471 | OptionDescription subflagContext, |
| 472 | ParsedOptionDescription currentSubflag, |
| 473 | FlagPolicyWithContext originalPolicy, |
| 474 | boolean isExpansion) |
Googler | 7c7255e | 2017-06-27 20:05:20 +0200 | [diff] [blame] | 475 | throws OptionsParsingException { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 476 | FlagPolicyWithContext subflagAsPolicy = null; |
| 477 | switch (originalPolicy.policy.getOperationCase()) { |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 478 | case SET_VALUE: |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 479 | if (currentSubflag.getOptionDefinition().allowsMultiple()) { |
ccalvarin | c331f24 | 2017-08-03 02:12:48 +0200 | [diff] [blame] | 480 | throw new AssertionError( |
| 481 | "SetValue subflags with allowMultiple should have been dealt with separately and " |
| 482 | + "accumulated into a single FlagPolicy."); |
| 483 | } |
| 484 | // Accept null originalValueStrings, they are expected when the subflag is also an expansion |
| 485 | // flag. |
| 486 | List<String> subflagValue; |
ccalvarin | a8c0c8d | 2017-09-14 16:54:39 +0200 | [diff] [blame] | 487 | if (currentSubflag.getUnconvertedValue() == null) { |
ccalvarin | c331f24 | 2017-08-03 02:12:48 +0200 | [diff] [blame] | 488 | subflagValue = ImmutableList.of(); |
| 489 | } else { |
ccalvarin | a8c0c8d | 2017-09-14 16:54:39 +0200 | [diff] [blame] | 490 | subflagValue = ImmutableList.of(currentSubflag.getUnconvertedValue()); |
ccalvarin | c331f24 | 2017-08-03 02:12:48 +0200 | [diff] [blame] | 491 | } |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 492 | subflagAsPolicy = |
| 493 | getSetValueSubflagAsPolicy( |
| 494 | subflagContext, subflagValue, currentSubflag.getOrigin(), originalPolicy); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 495 | break; |
| 496 | |
| 497 | case USE_DEFAULT: |
| 498 | // Commands from the original policy, flag name of the expansion |
| 499 | subflagAsPolicy = |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 500 | new FlagPolicyWithContext( |
| 501 | FlagPolicy.newBuilder() |
| 502 | .addAllCommands(originalPolicy.policy.getCommandsList()) |
| 503 | .setFlagName(currentSubflag.getOptionDefinition().getOptionName()) |
| 504 | .setUseDefault(UseDefault.getDefaultInstance()) |
| 505 | .build(), |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 506 | subflagContext, |
| 507 | currentSubflag.getOrigin()); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 508 | break; |
| 509 | |
| 510 | case ALLOW_VALUES: |
| 511 | if (isExpansion) { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 512 | throwAllowValuesOnExpansionFlagException(originalPolicy.policy.getFlagName()); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 513 | } |
| 514 | // If this flag is an implicitRequirement, and some values for the parent flag are |
| 515 | // allowed, nothing needs to happen on the implicitRequirement that is set for all |
| 516 | // values of the flag. |
| 517 | break; |
| 518 | |
| 519 | case DISALLOW_VALUES: |
| 520 | if (isExpansion) { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 521 | throwDisallowValuesOnExpansionFlagException(originalPolicy.policy.getFlagName()); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 522 | } |
| 523 | // If this flag is an implicitRequirement, and some values for the parent flag are |
| 524 | // disallowed, that implies that all others are allowed, so nothing needs to happen |
| 525 | // on the implicitRequirement that is set for all values of the parent flag. |
| 526 | break; |
| 527 | |
| 528 | case OPERATION_NOT_SET: |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 529 | throw new PolicyOperationNotSetException(originalPolicy.policy.getFlagName()); |
ccalvarin | ae1d0de | 2017-04-15 05:19:09 +0200 | [diff] [blame] | 530 | |
| 531 | default: |
| 532 | return null; |
| 533 | } |
| 534 | return subflagAsPolicy; |
| 535 | } |
| 536 | |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 537 | private static void applySetValueOperation( |
| 538 | OptionsParser parser, |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 539 | FlagPolicyWithContext flagPolicy, |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 540 | OptionValueDescription valueDescription, |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 541 | Level loglevel) |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 542 | throws OptionsParsingException { |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 543 | SetValue setValue = flagPolicy.policy.getSetValue(); |
| 544 | OptionDefinition optionDefinition = flagPolicy.description.getOptionDefinition(); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 545 | |
| 546 | // SetValue.flag_value must have at least 1 value. |
| 547 | if (setValue.getFlagValueCount() == 0) { |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 548 | throw new OptionsParsingException( |
| 549 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 550 | "SetValue operation from invocation policy for %s does not have a value", |
| 551 | optionDefinition)); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 552 | } |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 553 | |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 554 | // Flag must allow multiple values if multiple values are specified by the policy. |
ccalvarin | 0044349 | 2017-08-30 00:23:40 +0200 | [diff] [blame] | 555 | if (setValue.getFlagValueCount() > 1 |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 556 | && !flagPolicy.description.getOptionDefinition().allowsMultiple()) { |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 557 | throw new OptionsParsingException( |
| 558 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 559 | "SetValue operation from invocation policy sets multiple values for %s which " |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 560 | + "does not allow multiple values", |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 561 | optionDefinition)); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 562 | } |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 563 | |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 564 | switch (setValue.getBehavior()) { |
| 565 | case UNDEFINED: |
| 566 | throw throwUndefinedBehaviorException(flagPolicy.policy); |
| 567 | case ALLOW_OVERRIDES: |
| 568 | if (valueDescription != null) { |
| 569 | // The user set the value for the flag but the flag policy is overridable, so keep the |
| 570 | // user's |
| 571 | // value. |
| 572 | logger.at(loglevel).log( |
| 573 | "Keeping value '%s' from source '%s' for %s because the invocation policy specifying " |
| 574 | + "the value(s) '%s' is overridable", |
| 575 | valueDescription.getValue(), |
| 576 | valueDescription.getSourceString(), |
| 577 | optionDefinition, |
| 578 | setValue.getFlagValueList()); |
| 579 | // Nothing to do -- the value already has an override. |
| 580 | return; |
| 581 | } |
| 582 | break; |
| 583 | case FINAL_VALUE_IGNORE_OVERRIDES: |
Alex Humesky | 0595007 | 2016-05-09 18:38:55 +0000 | [diff] [blame] | 584 | // Clear the value in case the flag is a repeated flag so that values don't accumulate. |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 585 | parser.clearValue(flagPolicy.description.getOptionDefinition()); |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 586 | break; |
| 587 | case APPEND: |
| 588 | break; |
| 589 | } |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 590 | |
ajurkowski | ec66ded | 2021-10-27 10:39:52 -0700 | [diff] [blame] | 591 | // Set all the flag values from the policy. |
| 592 | for (String flagValue : setValue.getFlagValueList()) { |
| 593 | if (valueDescription == null) { |
| 594 | logger.at(loglevel).log( |
| 595 | "Setting value for %s from invocation policy to '%s', overriding the default value " |
| 596 | + "'%s'", |
| 597 | optionDefinition, flagValue, optionDefinition.getDefaultValue()); |
| 598 | } else { |
| 599 | logger.at(loglevel).log( |
| 600 | "Setting value for %s from invocation policy to '%s', overriding value '%s' from '%s'", |
| 601 | optionDefinition, |
| 602 | flagValue, |
| 603 | valueDescription.getValue(), |
| 604 | valueDescription.getSourceString()); |
| 605 | } |
Googler | 4442bb9 | 2021-10-15 09:15:28 -0700 | [diff] [blame] | 606 | |
ajurkowski | e88245d | 2021-06-10 18:00:38 -0700 | [diff] [blame] | 607 | parser.setOptionValueAtSpecificPriorityWithoutExpansion( |
| 608 | flagPolicy.origin, optionDefinition, flagValue); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 609 | } |
| 610 | } |
| 611 | |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 612 | private static void applyUseDefaultOperation( |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 613 | OptionsParser parser, String policyType, OptionDefinition option, Level loglevel) |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 614 | throws OptionsParsingException { |
| 615 | OptionValueDescription clearedValueDescription = parser.clearValue(option); |
ccalvarin | 0e02f53 | 2017-04-04 18:31:27 +0000 | [diff] [blame] | 616 | if (clearedValueDescription != null) { |
| 617 | // Log the removed value. |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 618 | String clearedFlagName = clearedValueDescription.getOptionDefinition().getOptionName(); |
ccalvarin | ca74482 | 2017-10-10 13:50:15 +0200 | [diff] [blame] | 619 | Object clearedFlagDefaultValue = |
| 620 | clearedValueDescription.getOptionDefinition().getDefaultValue(); |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 621 | logger.at(loglevel).log( |
| 622 | "Using default value '%s' for flag '%s' as specified by %s invocation policy, " |
| 623 | + "overriding original value '%s' from '%s'", |
| 624 | clearedFlagDefaultValue, |
| 625 | clearedFlagName, |
| 626 | policyType, |
| 627 | clearedValueDescription.getValue(), |
| 628 | clearedValueDescription.getSourceString()); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 629 | } |
| 630 | } |
| 631 | |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 632 | /** Checks the user's flag values against a filtering function. */ |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 633 | private abstract static class FilterValueOperation { |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 634 | |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 635 | private static final class AllowValueOperation extends FilterValueOperation { |
| 636 | AllowValueOperation(Level loglevel) { |
| 637 | super("Allow", loglevel); |
| 638 | } |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 639 | |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 640 | @Override |
| 641 | boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { |
| 642 | return convertedPolicyValues.contains(value); |
| 643 | } |
| 644 | } |
| 645 | |
| 646 | private static final class DisallowValueOperation extends FilterValueOperation { |
| 647 | DisallowValueOperation(Level loglevel) { |
| 648 | super("Disalllow", loglevel); |
| 649 | } |
| 650 | |
| 651 | @Override |
| 652 | boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value) { |
| 653 | // In a disallow operation, the values that the flag policy specifies are not allowed, |
| 654 | // so the value is allowed if the set of policy values does not contain the current |
| 655 | // flag value. |
| 656 | return !convertedPolicyValues.contains(value); |
| 657 | } |
| 658 | } |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 659 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 660 | private final String policyType; |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 661 | private final Level loglevel; |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 662 | |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 663 | FilterValueOperation(String policyType, Level loglevel) { |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 664 | this.policyType = policyType; |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 665 | this.loglevel = loglevel; |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 666 | } |
| 667 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 668 | /** |
| 669 | * Determines if the given value is allowed. |
| 670 | * |
| 671 | * @param convertedPolicyValues The values given from the FlagPolicy, converted to real objects. |
| 672 | * @param value The user value of the flag. |
| 673 | * @return True if the value should be allowed, false if it should not. |
| 674 | */ |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 675 | abstract boolean isFlagValueAllowed(Set<Object> convertedPolicyValues, Object value); |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 676 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 677 | void apply( |
| 678 | OptionsParser parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 679 | OptionInstanceOrigin origin, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 680 | List<String> policyValues, |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 681 | String newValue, |
| 682 | boolean useDefault, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 683 | OptionValueDescription valueDescription, |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 684 | OptionDescription optionDescription) |
| 685 | throws OptionsParsingException { |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 686 | OptionDefinition optionDefinition = optionDescription.getOptionDefinition(); |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 687 | // Convert all the allowed values from strings to real objects using the options' |
| 688 | // converters so that they can be checked for equality using real .equals() instead |
| 689 | // of string comparison. For example, "--foo=0", "--foo=false", "--nofoo", and "-f-" |
| 690 | // (if the option has an abbreviation) are all equal for boolean flags. Plus converters |
| 691 | // can be arbitrarily complex. |
Jonathan Bluett-Duncan | 0df3ddbd | 2017-08-09 11:13:54 +0200 | [diff] [blame] | 692 | Set<Object> convertedPolicyValues = new HashSet<>(); |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 693 | for (String value : policyValues) { |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 694 | Object convertedValue = optionDefinition.getConverter().convert(value); |
Alex Humesky | 6415587 | 2016-12-16 22:12:52 +0000 | [diff] [blame] | 695 | // Some converters return lists, and if the flag is a repeatable flag, the items in the |
| 696 | // list from the converter should be added, and not the list itself. Otherwise the items |
| 697 | // from invocation policy will be compared to lists, which will never work. |
| 698 | // See OptionsParserImpl.ParsedOptionEntry.addValue. |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 699 | if (optionDefinition.allowsMultiple() && convertedValue instanceof List<?>) { |
Alex Humesky | 6415587 | 2016-12-16 22:12:52 +0000 | [diff] [blame] | 700 | convertedPolicyValues.addAll((List<?>) convertedValue); |
| 701 | } else { |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 702 | convertedPolicyValues.add(optionDefinition.getConverter().convert(value)); |
Alex Humesky | 6415587 | 2016-12-16 22:12:52 +0000 | [diff] [blame] | 703 | } |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 704 | } |
| 705 | |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 706 | // Check that if the default value of the flag is disallowed by the policy, that the policy |
ccalvarin | 06e6874 | 2018-03-01 07:29:45 -0800 | [diff] [blame] | 707 | // does not also set use_default. Otherwise the default value would still be set if the |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 708 | // user uses a disallowed value. This doesn't apply to repeatable flags since the default |
ccalvarin | 06e6874 | 2018-03-01 07:29:45 -0800 | [diff] [blame] | 709 | // value for repeatable flags is always the empty list. It also doesn't apply to flags that |
| 710 | // are null by default, since these flags' default value is not parsed by the converter, so |
| 711 | // there is no guarantee that there exists an accepted user-input value that would also set |
| 712 | // the value to NULL. In these cases, we assume that "unset" is a distinct value that is |
| 713 | // always allowed. |
| 714 | if (!optionDescription.getOptionDefinition().allowsMultiple() |
| 715 | && !optionDescription.getOptionDefinition().isSpecialNullDefault()) { |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 716 | boolean defaultValueAllowed = |
ccalvarin | 0044349 | 2017-08-30 00:23:40 +0200 | [diff] [blame] | 717 | isFlagValueAllowed( |
| 718 | convertedPolicyValues, optionDescription.getOptionDefinition().getDefaultValue()); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 719 | if (!defaultValueAllowed && useDefault) { |
| 720 | throw new OptionsParsingException( |
| 721 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 722 | "%sValues policy disallows the default value '%s' for %s but also specifies to " |
| 723 | + "use the default value", |
| 724 | policyType, optionDefinition.getDefaultValue(), optionDefinition)); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 725 | } |
| 726 | } |
| 727 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 728 | if (valueDescription == null) { |
| 729 | // Nothing has set the value yet, so check that the default value from the flag's |
| 730 | // definition is allowed. The else case below (i.e. valueDescription is not null) checks for |
| 731 | // the flag allowing multiple values, however, flags that allow multiple values cannot have |
| 732 | // default values, and their value is always the empty list if they haven't been specified, |
| 733 | // which is why new_default_value is not a repeated field. |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 734 | checkDefaultValue( |
| 735 | parser, origin, optionDescription, policyValues, newValue, convertedPolicyValues); |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 736 | } else { |
| 737 | checkUserValue( |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 738 | parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 739 | origin, |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 740 | optionDescription, |
| 741 | valueDescription, |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 742 | policyValues, |
| 743 | newValue, |
| 744 | useDefault, |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 745 | convertedPolicyValues); |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 746 | } |
| 747 | } |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 748 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 749 | void checkDefaultValue( |
| 750 | OptionsParser parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 751 | OptionInstanceOrigin origin, |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 752 | OptionDescription optionDescription, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 753 | List<String> policyValues, |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 754 | String newValue, |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 755 | Set<Object> convertedPolicyValues) |
| 756 | throws OptionsParsingException { |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 757 | |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 758 | OptionDefinition optionDefinition = optionDescription.getOptionDefinition(); |
ccalvarin | 06e6874 | 2018-03-01 07:29:45 -0800 | [diff] [blame] | 759 | if (optionDefinition.isSpecialNullDefault()) { |
| 760 | // Do nothing, the unset value by definition cannot be set. In option filtering operations, |
| 761 | // the value is being filtered, but the value that is `no value` passes any filter. |
| 762 | // Otherwise, there is no way to "usedefault" on one of these options that has no value by |
| 763 | // default. |
| 764 | } else if (!isFlagValueAllowed(convertedPolicyValues, optionDefinition.getDefaultValue())) { |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 765 | if (newValue != null) { |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 766 | // Use the default value from the policy, since the original default is not allowed |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 767 | logger.at(loglevel).log( |
| 768 | "Overriding default value '%s' for %s with value '%s' specified by invocation " |
| 769 | + "policy. %sed values are: %s", |
| 770 | optionDefinition.getDefaultValue(), |
| 771 | optionDefinition, |
| 772 | newValue, |
| 773 | policyType, |
| 774 | policyValues); |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 775 | parser.clearValue(optionDefinition); |
ajurkowski | e88245d | 2021-06-10 18:00:38 -0700 | [diff] [blame] | 776 | parser.setOptionValueAtSpecificPriorityWithoutExpansion( |
| 777 | origin, optionDefinition, newValue); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 778 | } else { |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 779 | // The operation disallows the default value, but doesn't supply a new value. |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 780 | throw new OptionsParsingException( |
| 781 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 782 | "Default flag value '%s' for %s is not allowed by invocation policy, but " |
ccalvarin | 3ab171a | 2017-09-19 16:36:49 +0200 | [diff] [blame] | 783 | + "the policy does not provide a new value. %sed values are: %s", |
ccalvarin | 0044349 | 2017-08-30 00:23:40 +0200 | [diff] [blame] | 784 | optionDescription.getOptionDefinition().getDefaultValue(), |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 785 | optionDefinition, |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 786 | policyType, |
| 787 | policyValues)); |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 788 | } |
| 789 | } |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 790 | } |
Luis Fernando Pino Duque | b1b28b6 | 2016-02-25 14:25:19 +0000 | [diff] [blame] | 791 | |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 792 | void checkUserValue( |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 793 | OptionsParser parser, |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 794 | OptionInstanceOrigin origin, |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 795 | OptionDescription optionDescription, |
| 796 | OptionValueDescription valueDescription, |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 797 | List<String> policyValues, |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 798 | String newValue, |
| 799 | boolean useDefault, |
Janak Ramakrishnan | b92c097 | 2016-03-23 16:47:13 +0000 | [diff] [blame] | 800 | Set<Object> convertedPolicyValues) |
| 801 | throws OptionsParsingException { |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 802 | OptionDefinition option = optionDescription.getOptionDefinition(); |
ccalvarin | 0044349 | 2017-08-30 00:23:40 +0200 | [diff] [blame] | 803 | if (optionDescription.getOptionDefinition().allowsMultiple()) { |
Alex Humesky | c0d2769 | 2016-02-03 00:52:04 +0000 | [diff] [blame] | 804 | // allowMultiple requires that the type of the option be List<T>, so cast from Object |
| 805 | // to List<?>. |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 806 | List<?> optionValues = (List<?>) valueDescription.getValue(); |
| 807 | for (Object value : optionValues) { |
| 808 | if (!isFlagValueAllowed(convertedPolicyValues, value)) { |
| 809 | if (useDefault) { |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 810 | applyUseDefaultOperation(parser, policyType + "Values", option, loglevel); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 811 | } else { |
| 812 | throw new OptionsParsingException( |
| 813 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 814 | "Flag value '%s' for %s is not allowed by invocation policy. %sed values " |
| 815 | + "are: %s", |
| 816 | value, option, policyType, policyValues)); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 817 | } |
| 818 | } |
| 819 | } |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 820 | |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 821 | } else { |
| 822 | |
| 823 | if (!isFlagValueAllowed(convertedPolicyValues, valueDescription.getValue())) { |
| 824 | if (newValue != null) { |
janakr | c956021 | 2020-05-27 15:07:36 -0700 | [diff] [blame] | 825 | logger.at(loglevel).log( |
| 826 | "Overriding disallowed value '%s' for %s with value '%s' " |
| 827 | + "specified by invocation policy. %sed values are: %s", |
| 828 | valueDescription.getValue(), option, newValue, policyType, policyValues); |
ccalvarin | 1dce097 | 2017-09-11 20:03:02 +0200 | [diff] [blame] | 829 | parser.clearValue(option); |
ajurkowski | e88245d | 2021-06-10 18:00:38 -0700 | [diff] [blame] | 830 | parser.setOptionValueAtSpecificPriorityWithoutExpansion(origin, option, newValue); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 831 | } else if (useDefault) { |
ccalvarin | 334d2f1 | 2017-10-05 16:39:42 +0200 | [diff] [blame] | 832 | applyUseDefaultOperation(parser, policyType + "Values", option, loglevel); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 833 | } else { |
| 834 | throw new OptionsParsingException( |
| 835 | String.format( |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 836 | "Flag value '%s' for %s is not allowed by invocation policy and the " |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 837 | + "policy does not specify a new value. %sed values are: %s", |
ccalvarin | 7cd9e88 | 2017-10-16 22:18:32 +0200 | [diff] [blame] | 838 | valueDescription.getValue(), option, policyType, policyValues)); |
Alex Humesky | b1f55c8 | 2016-10-25 01:06:39 +0000 | [diff] [blame] | 839 | } |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 840 | } |
| 841 | } |
| 842 | } |
| 843 | } |
Alex Humesky | 2f3f4cf | 2015-09-29 01:42:00 +0000 | [diff] [blame] | 844 | } |