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