1 // Copyright 2017 The Bazel Authors. All rights reserved. 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. 14 15 package com.google.devtools.common.options; 16 17 import com.google.common.collect.ImmutableList; 18 import java.util.function.Function; 19 import javax.annotation.Nullable; 20 21 /** 22 * The representation of a parsed option instance. 23 * 24 * <p>An option instance is distinct from the final value of an option, as multiple instances 25 * provide values may be overridden or combined in some way. 26 */ 27 public final class ParsedOptionDescription { 28 29 private final OptionDefinition optionDefinition; 30 private final String commandLineForm; 31 @Nullable private final String unconvertedValue; 32 private final OptionInstanceOrigin origin; 33 34 public ParsedOptionDescription( 35 OptionDefinition optionDefinition, 36 String commandLineForm, 37 @Nullable String unconvertedValue, 38 OptionInstanceOrigin origin) { 39 this.optionDefinition = optionDefinition; 40 this.commandLineForm = commandLineForm; 41 this.unconvertedValue = unconvertedValue; 42 this.origin = origin; 43 } 44 45 public OptionDefinition getOptionDefinition() { 46 return optionDefinition; 47 } 48 49 public String getCommandLineForm() { 50 return commandLineForm; 51 } 52 53 public String getCanonicalForm() { 54 return getCanonicalFormWithValueEscaper(s -> s); 55 } 56 57 public String getCanonicalFormWithValueEscaper(Function<String, String> escapingFunction) { 58 // For boolean flags (note that here we do not check for TriState flags, only flags with actual 59 // boolean values, so that we know the return type of getConvertedValue), use the --[no]flag 60 // form for the canonical value. 61 if (optionDefinition.getType().equals(boolean.class)) { 62 try { 63 return ((boolean) getConvertedValue() ? "--" : "--no") + optionDefinition.getOptionName(); 64 } catch (OptionsParsingException e) { 65 throw new RuntimeException("Unexpected parsing exception", e); 66 } 67 } else { 68 String optionString = "--" + optionDefinition.getOptionName(); 69 if (unconvertedValue != null) { // Can be null for Void options. 70 optionString += "=" + escapingFunction.apply(unconvertedValue); 71 } 72 return optionString; 73 } 74 } 75 76 @Deprecated 77 // TODO(b/65646296) Once external dependencies are cleaned up, use getCanonicalForm() 78 String getDeprecatedCanonicalForm() { 79 String value = unconvertedValue; 80 // For boolean flags (note that here we do not check for TriState flags, only flags with actual 81 // boolean values, so that we know the return type of getConvertedValue), set them all to 1 or 82 // 0, instead of keeping the wide variety of values we accept in their original form. 83 if (optionDefinition.getType().equals(boolean.class)) { 84 try { 85 value = (boolean) getConvertedValue() ? "1" : "0"; 86 } catch (OptionsParsingException e) { 87 throw new RuntimeException("Unexpected parsing exception", e); 88 } 89 } 90 return String.format("--%s=%s", optionDefinition.getOptionName(), value); 91 } 92 93 public boolean isBooleanOption() { 94 return optionDefinition.getType().equals(boolean.class); 95 } 96 97 private OptionDocumentationCategory documentationCategory() { 98 return optionDefinition.getDocumentationCategory(); 99 } 100 101 private ImmutableList<OptionMetadataTag> metadataTags() { 102 return ImmutableList.copyOf(optionDefinition.getOptionMetadataTags()); 103 } 104 105 public boolean isDocumented() { 106 return documentationCategory() != OptionDocumentationCategory.UNDOCUMENTED && !isHidden(); 107 } 108 109 public boolean isHidden() { 110 ImmutableList<OptionMetadataTag> tags = metadataTags(); 111 return tags.contains(OptionMetadataTag.HIDDEN) || tags.contains(OptionMetadataTag.INTERNAL); 112 } 113 114 public String getUnconvertedValue() { 115 return unconvertedValue; 116 } 117 118 public OptionInstanceOrigin getOrigin() { 119 return origin; 120 } 121 122 public OptionPriority getPriority() { 123 return origin.getPriority(); 124 } 125 126 public String getSource() { 127 return origin.getSource(); 128 } 129 130 OptionDefinition getImplicitDependent() { 131 return origin.getImplicitDependent(); 132 } 133 134 OptionDefinition getExpandedFrom() { 135 return origin.getExpandedFrom(); 136 } 137 138 public boolean isExplicit() { 139 return origin.getExpandedFrom() == null && origin.getImplicitDependent() == null; 140 } 141 142 public Object getConvertedValue() throws OptionsParsingException { 143 Converter<?> converter = optionDefinition.getConverter(); 144 try { 145 return converter.convert(unconvertedValue); 146 } catch (OptionsParsingException e) { 147 // The converter doesn't know the option name, so we supply it here by re-throwing: 148 throw new OptionsParsingException( 149 String.format("While parsing option %s: %s", commandLineForm, e.getMessage()), e); 150 } 151 } 152 153 @Override 154 public String toString() { 155 StringBuilder result = new StringBuilder(); 156 result.append(optionDefinition); 157 result.append("set to '").append(unconvertedValue).append("' "); 158 result.append("with priority ").append(origin.getPriority()); 159 if (origin.getSource() != null) { 160 result.append(" and source '").append(origin.getSource()).append("'"); 161 } 162 return result.toString(); 163 } 164 165 } 166