All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.devtools.common.options.OptionsBase Maven / Gradle / Ivy

// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.devtools.common.options;

import com.google.common.collect.Maps;
import com.google.common.escape.CharEscaperBuilder;
import com.google.common.escape.Escaper;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * Base class for all options classes. Extend this class, adding public instance fields annotated
 * with {@link Option}. Then you can create instances either programmatically:
 *
 * 
 *   X x = Options.getDefaults(X.class);
 *   x.host = "localhost";
 *   x.port = 80;
 * 
* * or from an array of command-line arguments: * *
 *   OptionsParser parser = OptionsParser.builder()
 *       .optionsClasses(X.class)
 *       .build();
 *   parser.parse("--host", "localhost", "--port", "80");
 *   X x = parser.getOptions(X.class);
 * 
* *

Subclasses of {@code OptionsBase} must be constructed reflectively, i.e. using not * {@code new MyOptions()}, but one of the above methods instead. (Direct construction creates an * empty instance, not containing default values. This leads to surprising behavior and often {@code * NullPointerExceptions}, etc.) */ public abstract class OptionsBase { private static final Escaper ESCAPER = new CharEscaperBuilder() .addEscape('\\', "\\\\").addEscape('"', "\\\"").toEscaper(); /** * Subclasses must provide a default (no argument) constructor. */ protected OptionsBase() { // There used to be a sanity check here that checks the stack trace of this constructor // invocation; unfortunately, that makes the options construction about 10x slower. So be // careful with how you construct options classes. } /** * Returns a mapping from option names to values, for each option on this object, including * inherited ones. The mapping is a copy, so subsequent mutations to it or to this object are * independent. Entries are sorted alphabetically. */ public final Map asMap() { List definitions = OptionsData.getAllOptionDefinitionsForClass(getClass()); Map map = Maps.newLinkedHashMapWithExpectedSize(definitions.size()); for (OptionDefinition definition : definitions) { map.put(definition.getOptionName(), getValueFromDefinition(definition)); } return map; } /** Returns the value of the option described by {@code definition}. */ public final Object getValueFromDefinition(OptionDefinition definition) { try { return definition.getField().get(this); } catch (IllegalAccessException e) { throw new IllegalStateException("All options fields of options classes should be public", e); } } @Override public final String toString() { return getClass().getName() + asMap(); } /** * Returns a string that uniquely identifies the options. This value is * intended for analysis caching. */ public final String cacheKey() { StringBuilder result = new StringBuilder(getClass().getName()).append("{"); result.append(mapToCacheKey(asMap())); return result.append("}").toString(); } public static String mapToCacheKey(Map optionsMap) { StringBuilder result = new StringBuilder(); for (Map.Entry entry : optionsMap.entrySet()) { result.append(entry.getKey()).append("="); Object value = entry.getValue(); // This special case is needed because List.toString() prints the same // ("[]") for an empty list and for a list with a single empty string. if (value instanceof List && ((List) value).isEmpty()) { result.append("EMPTY"); } else if (value == null) { result.append("NULL"); } else { result .append('"') .append(ESCAPER.escape(value.toString())) .append('"'); } result.append(", "); } return result.toString(); } @Override @SuppressWarnings("EqualsGetClass") // Options can only be equal if they are of the same type. public final boolean equals(Object that) { if (this == that) { return true; } if (that == null || !getClass().equals(that.getClass())) { return false; } OptionsBase other = (OptionsBase) that; for (OptionDefinition def : OptionsParser.getOptionDefinitions(getClass())) { if (!Objects.equals(getValueFromDefinition(def), other.getValueFromDefinition(def))) { return false; } } return true; } @Override public final int hashCode() { return this.getClass().hashCode() + asMap().hashCode(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy