18c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// Copyright 2017 The Bazel Authors. All rights reserved.
28c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin//
38c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// Licensed under the Apache License, Version 2.0 (the "License");
48c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// you may not use this file except in compliance with the License.
58c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// You may obtain a copy of the License at
68c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin//
78c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin//    http://www.apache.org/licenses/LICENSE-2.0
88c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin//
98c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// Unless required by applicable law or agreed to in writing, software
108c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// distributed under the License is distributed on an "AS IS" BASIS,
118c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
128c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// See the License for the specific language governing permissions and
138c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin// limitations under the License.
148c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
158c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarinpackage com.google.devtools.common.options;
168c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
178c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarinimport com.google.common.collect.ImmutableList;
18a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarinimport java.util.function.Function;
198c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarinimport javax.annotation.Nullable;
208c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
218c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin/**
2205bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin * The representation of a parsed option instance.
238c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin *
2405bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin * <p>An option instance is distinct from the final value of an option, as multiple instances
2505bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin * provide values may be overridden or combined in some way.
268c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin */
27cff0d218a8d061b3568a6197b8d456424d9c9c4accalvarinpublic final class ParsedOptionDescription {
2805bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin
298c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  private final OptionDefinition optionDefinition;
30be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin  private final String commandLineForm;
31bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin  @Nullable private final String unconvertedValue;
3205bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin  private final OptionInstanceOrigin origin;
338c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
34cff0d218a8d061b3568a6197b8d456424d9c9c4accalvarin  public ParsedOptionDescription(
358c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin      OptionDefinition optionDefinition,
36be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin      String commandLineForm,
37bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin      @Nullable String unconvertedValue,
3805bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin      OptionInstanceOrigin origin) {
398c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    this.optionDefinition = optionDefinition;
40be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    this.commandLineForm = commandLineForm;
41bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin    this.unconvertedValue = unconvertedValue;
4205bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    this.origin = origin;
438c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
448c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
45bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin  public OptionDefinition getOptionDefinition() {
468c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return optionDefinition;
478c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
488c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
49be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin  public String getCommandLineForm() {
50be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    return commandLineForm;
51be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin  }
52be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin
53a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  public String getCanonicalForm() {
54a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    return getCanonicalFormWithValueEscaper(s -> s);
55a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  }
56a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin
57a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  public String getCanonicalFormWithValueEscaper(Function<String, String> escapingFunction) {
58a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // For boolean flags (note that here we do not check for TriState flags, only flags with actual
59a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // boolean values, so that we know the return type of getConvertedValue), use the --[no]flag
60a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // form for the canonical value.
61a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    if (optionDefinition.getType().equals(boolean.class)) {
62a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      try {
63a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin        return ((boolean) getConvertedValue() ? "--" : "--no") + optionDefinition.getOptionName();
64a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      } catch (OptionsParsingException e) {
65a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin        throw new RuntimeException("Unexpected parsing exception", e);
66a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      }
67a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    } else {
68a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      String optionString = "--" + optionDefinition.getOptionName();
69a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      if (unconvertedValue != null) { // Can be null for Void options.
70a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin        optionString += "=" + escapingFunction.apply(unconvertedValue);
71a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      }
72a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      return optionString;
73a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    }
74a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  }
75a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin
76a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  @Deprecated
77a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  // TODO(b/65646296) Once external dependencies are cleaned up, use getCanonicalForm()
78a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  String getDeprecatedCanonicalForm() {
79a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    String value = unconvertedValue;
80a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // For boolean flags (note that here we do not check for TriState flags, only flags with actual
81a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // boolean values, so that we know the return type of getConvertedValue), set them all to 1 or
82a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    // 0, instead of keeping the wide variety of values we accept in their original form.
83a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    if (optionDefinition.getType().equals(boolean.class)) {
84a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      try {
85a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin        value = (boolean) getConvertedValue() ? "1" : "0";
86a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      } catch (OptionsParsingException e) {
87a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin        throw new RuntimeException("Unexpected parsing exception", e);
88a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin      }
89a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    }
90a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin    return String.format("--%s=%s", optionDefinition.getOptionName(), value);
91a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin  }
92a0d433e79adbd70bc30ee613654fdf3fbb9de991ccalvarin
938c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public boolean isBooleanOption() {
948c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return optionDefinition.getType().equals(boolean.class);
958c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
968c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
978c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  private OptionDocumentationCategory documentationCategory() {
988c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return optionDefinition.getDocumentationCategory();
998c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1008c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
1018c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  private ImmutableList<OptionMetadataTag> metadataTags() {
1028c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return ImmutableList.copyOf(optionDefinition.getOptionMetadataTags());
1038c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1048c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
1058c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public boolean isDocumented() {
1068c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return documentationCategory() != OptionDocumentationCategory.UNDOCUMENTED && !isHidden();
1078c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1088c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
1098c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public boolean isHidden() {
1108c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    ImmutableList<OptionMetadataTag> tags = metadataTags();
1118c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return tags.contains(OptionMetadataTag.HIDDEN) || tags.contains(OptionMetadataTag.INTERNAL);
1128c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1138c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
114bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin  public String getUnconvertedValue() {
115bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin    return unconvertedValue;
1168c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1178c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
118195dd40824457bb0b3b934ec2c14ab36840f1ff0ccalvarin  public OptionInstanceOrigin getOrigin() {
119195dd40824457bb0b3b934ec2c14ab36840f1ff0ccalvarin    return origin;
120195dd40824457bb0b3b934ec2c14ab36840f1ff0ccalvarin  }
121195dd40824457bb0b3b934ec2c14ab36840f1ff0ccalvarin
1222c2f636ed26b6cf80d2135f0d337e159e047e89dccalvarin  public OptionPriority getPriority() {
12305bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    return origin.getPriority();
1248c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1258c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
1268c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public String getSource() {
12705bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    return origin.getSource();
12805bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin  }
12905bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin
13005bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin  OptionDefinition getImplicitDependent() {
13105bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    return origin.getImplicitDependent();
13205bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin  }
13305bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin
13405bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin  OptionDefinition getExpandedFrom() {
13505bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    return origin.getExpandedFrom();
1368c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1378c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
1388c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public boolean isExplicit() {
13905bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    return origin.getExpandedFrom() == null && origin.getImplicitDependent() == null;
1408c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1418c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin
142be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin  public Object getConvertedValue() throws OptionsParsingException {
143be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    Converter<?> converter = optionDefinition.getConverter();
144be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    try {
145be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin      return converter.convert(unconvertedValue);
146be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    } catch (OptionsParsingException e) {
147be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin      // The converter doesn't know the option name, so we supply it here by re-throwing:
148be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin      throw new OptionsParsingException(
149be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin          String.format("While parsing option %s: %s", commandLineForm, e.getMessage()), e);
150be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin    }
151be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin  }
152be286cfc3497bf329a5224f56b3e2d6a451c243dccalvarin
1538c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  @Override
1548c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  public String toString() {
1558c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    StringBuilder result = new StringBuilder();
156195dd40824457bb0b3b934ec2c14ab36840f1ff0ccalvarin    result.append(optionDefinition);
157bf096ba935f132b5da0ad941f41897f7a7409fb3ccalvarin    result.append("set to '").append(unconvertedValue).append("' ");
15805bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    result.append("with priority ").append(origin.getPriority());
15905bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin    if (origin.getSource() != null) {
16005bb5c28c6be8320c06d8bb1bafd0be586678138ccalvarin      result.append(" and source '").append(origin.getSource()).append("'");
1618c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    }
1628c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin    return result.toString();
1638c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin  }
1642c366b859ba2c84184e38450786e33368c9c39c2ccalvarin
1658c3292b0ae37edbbc3470d3a3ad34fffd2aef413ccalvarin}
166