1 // Copyright 2014 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 package com.google.devtools.common.options; 15 16 import com.google.common.collect.ImmutableList; 17 import java.util.Objects; 18 19 /** 20 * The position of an option in the interpretation order. Options are interpreted using a 21 * last-option-wins system for single valued options, and are listed in that order for 22 * multiple-valued options. 23 * 24 * <p>The position of the option is in category order, and within the priority category in index 25 * order. 26 */ 27 public class OptionPriority implements Comparable<OptionPriority> { 28 private final PriorityCategory priorityCategory; 29 /** 30 * Each option that is passed explicitly has 0 ancestors, so it only has its command line index 31 * (or rc index, etc., depending on the category), but expanded options have the command line 32 * index of its parent and then its position within the options that were expanded at that point. 33 * Since options can expand to expanding options, and --config can expand to expansion options as 34 * well, this can technically go arbitrarily deep, but in practice this is very short, of length < 35 * 5, most commonly of length 1. 36 */ 37 private final ImmutableList<Integer> priorityIndices; 38 39 private boolean alreadyExpanded = false; 40 41 private OptionPriority( 42 PriorityCategory priorityCategory, ImmutableList<Integer> priorityIndices) { 43 this.priorityCategory = priorityCategory; 44 this.priorityIndices = priorityIndices; 45 } 46 47 /** Get the first OptionPriority for that category. */ 48 static OptionPriority lowestOptionPriorityAtCategory(PriorityCategory category) { 49 return new OptionPriority(category, ImmutableList.of(0)); 50 } 51 52 /** 53 * Get the priority for the option following this one. In normal, incremental option parsing, the 54 * returned priority would compareTo as after the current one. Does not increment ancestor 55 * priorities. 56 */ 57 static OptionPriority nextOptionPriority(OptionPriority priority) { 58 int lastElementPosition = priority.priorityIndices.size() - 1; 59 return new OptionPriority( 60 priority.priorityCategory, 61 ImmutableList.<Integer>builder() 62 .addAll(priority.priorityIndices.subList(0, lastElementPosition)) 63 .add(priority.priorityIndices.get(lastElementPosition) + 1) 64 .build()); 65 } 66 67 /** 68 * Some options are expanded to other options, and the children options need to have their order 69 * preserved while maintaining their position between the options that flank the parent option. 70 * 71 * @return the priority for the first child of the passed priority. This child's ordering can be 72 * tracked the same way that the parent's was. 73 */ 74 public static OptionPriority getChildPriority(OptionPriority parentPriority) 75 throws OptionsParsingException { 76 if (parentPriority.alreadyExpanded) { 77 throw new OptionsParsingException("Tried to expand option too many times"); 78 } 79 // Prevent this option from being re-expanded. 80 parentPriority.alreadyExpanded = true; 81 82 // The child priority has 1 more level of nesting than its parent. 83 return new OptionPriority( 84 parentPriority.priorityCategory, 85 ImmutableList.<Integer>builder().addAll(parentPriority.priorityIndices).add(0).build()); 86 } 87 88 public PriorityCategory getPriorityCategory() { 89 return priorityCategory; 90 } 91 92 @Override 93 public int compareTo(OptionPriority o) { 94 if (priorityCategory.equals(o.priorityCategory)) { 95 for (int i = 0; i < priorityIndices.size() && i < o.priorityIndices.size(); ++i) { 96 if (!priorityIndices.get(i).equals(o.priorityIndices.get(i))) { 97 return priorityIndices.get(i).compareTo(o.priorityIndices.get(i)); 98 } 99 } 100 // The values are up to the shorter one's length are the same, so the shorter one is a direct 101 // ancestor and comes first. 102 return Integer.compare(priorityIndices.size(), o.priorityIndices.size()); 103 } 104 return Integer.compare(priorityCategory.ordinal(), o.priorityCategory.ordinal()); 105 } 106 107 @Override 108 public boolean equals(Object o) { 109 if (o instanceof OptionPriority) { 110 OptionPriority other = (OptionPriority) o; 111 return priorityCategory.equals(other.priorityCategory) 112 && priorityIndices.equals(other.priorityIndices); 113 } 114 return false; 115 } 116 117 @Override 118 public int hashCode() { 119 return Objects.hash(priorityCategory, priorityIndices); 120 } 121 122 @Override 123 public String toString() { 124 return String.format("OptionPriority(%s,%s)", priorityCategory, priorityIndices); 125 } 126 127 /** 128 * The priority of option values, in order of increasing priority. 129 * 130 * <p>In general, new values for options can only override values with a lower or equal priority. 131 * Option values provided in annotations in an options class are implicitly at the priority {@code 132 * DEFAULT}. 133 * 134 * <p>The ordering of the priorities is the source-code order. This is consistent with the 135 * automatically generated {@code compareTo} method as specified by the Java Language 136 * Specification. DO NOT change the source-code order of these values, or you will break code that 137 * relies on the ordering. 138 */ 139 public enum PriorityCategory { 140 141 /** 142 * The priority of values specified in the {@link Option} annotation. This should never be 143 * specified in calls to {@link OptionsParser#parse}. 144 */ 145 DEFAULT, 146 147 /** 148 * Overrides default options at runtime, while still allowing the values to be overridden 149 * manually. 150 */ 151 COMPUTED_DEFAULT, 152 153 /** For options coming from a configuration file or rc file. */ 154 RC_FILE, 155 156 /** For options coming from the command line. */ 157 COMMAND_LINE, 158 159 /** For options coming from invocation policy. */ 160 INVOCATION_POLICY, 161 162 /** 163 * This priority can be used to unconditionally override any user-provided options. This should 164 * be used rarely and with caution! 165 */ 166 SOFTWARE_REQUIREMENT 167 } 168 } 169