diff options
Diffstat (limited to 'src/main/java/com/google/devtools/common/options/OptionsUsage.java')
-rw-r--r-- | src/main/java/com/google/devtools/common/options/OptionsUsage.java | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/common/options/OptionsUsage.java b/src/main/java/com/google/devtools/common/options/OptionsUsage.java new file mode 100644 index 0000000000..c48a53295c --- /dev/null +++ b/src/main/java/com/google/devtools/common/options/OptionsUsage.java @@ -0,0 +1,156 @@ +// Copyright 2014 Google Inc. 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 static com.google.devtools.common.options.OptionsParserImpl.findConverter; + +import com.google.common.base.Splitter; +import com.google.common.base.Strings; +import com.google.common.collect.Lists; + +import java.lang.reflect.Field; +import java.text.BreakIterator; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * A renderer for usage messages. For now this is very simple. + */ +class OptionsUsage { + + private static final Splitter NEWLINE_SPLITTER = Splitter.on('\n'); + + /** + * Given an options class, render the usage string into the usage, + * which is passed in as an argument. + */ + static void getUsage(Class<? extends OptionsBase> optionsClass, StringBuilder usage) { + List<Field> optionFields = + Lists.newArrayList(OptionsParser.getAllAnnotatedFields(optionsClass)); + Collections.sort(optionFields, BY_NAME); + for (Field optionField : optionFields) { + getUsage(optionField, usage, OptionsParser.HelpVerbosity.LONG); + } + } + + /** + * Paragraph-fill the specified input text, indenting lines to 'indent' and + * wrapping lines at 'width'. Returns the formatted result. + */ + static String paragraphFill(String in, int indent, int width) { + String indentString = Strings.repeat(" ", indent); + StringBuilder out = new StringBuilder(); + String sep = ""; + for (String paragraph : NEWLINE_SPLITTER.split(in)) { + BreakIterator boundary = BreakIterator.getLineInstance(); // (factory) + boundary.setText(paragraph); + out.append(sep).append(indentString); + int cursor = indent; + for (int start = boundary.first(), end = boundary.next(); + end != BreakIterator.DONE; + start = end, end = boundary.next()) { + String word = + paragraph.substring(start, end); // (may include trailing space) + if (word.length() + cursor > width) { + out.append('\n').append(indentString); + cursor = indent; + } + out.append(word); + cursor += word.length(); + } + sep = "\n"; + } + return out.toString(); + } + + /** + * Append the usage message for a single option-field message to 'usage'. + */ + static void getUsage(Field optionField, StringBuilder usage, + OptionsParser.HelpVerbosity helpVerbosity) { + String flagName = getFlagName(optionField); + String typeDescription = getTypeDescription(optionField); + Option annotation = optionField.getAnnotation(Option.class); + usage.append(" --" + flagName); + if (helpVerbosity == OptionsParser.HelpVerbosity.SHORT) { // just the name + usage.append('\n'); + return; + } + if (annotation.abbrev() != '\0') { + usage.append(" [-").append(annotation.abbrev()).append(']'); + } + if (!typeDescription.equals("")) { + usage.append(" (" + typeDescription + "; "); + if (annotation.allowMultiple()) { + usage.append("may be used multiple times"); + } else { + // Don't call the annotation directly (we must allow overrides to certain defaults) + String defaultValueString = OptionsParserImpl.getDefaultOptionString(optionField); + if (OptionsParserImpl.isSpecialNullDefault(defaultValueString, optionField)) { + usage.append("default: see description"); + } else { + usage.append("default: \"" + defaultValueString + "\""); + } + } + usage.append(")"); + } + usage.append("\n"); + if (helpVerbosity == OptionsParser.HelpVerbosity.MEDIUM) { // just the name and type. + return; + } + if (!annotation.help().equals("")) { + usage.append(paragraphFill(annotation.help(), 4, 80)); // (indent, width) + usage.append('\n'); + } + if (annotation.expansion().length > 0) { + StringBuilder expandsMsg = new StringBuilder("Expands to: "); + for (String exp : annotation.expansion()) { + expandsMsg.append(exp).append(" "); + } + usage.append(paragraphFill(expandsMsg.toString(), 4, 80)); // (indent, width) + usage.append('\n'); + } + } + + private static final Comparator<Field> BY_NAME = new Comparator<Field>() { + @Override + public int compare(Field left, Field right) { + return left.getName().compareTo(right.getName()); + } + }; + + /** + * An ordering relation for option-field fields that first groups together + * options of the same category, then sorts by name within the category. + */ + static final Comparator<Field> BY_CATEGORY = new Comparator<Field>() { + @Override + public int compare(Field left, Field right) { + int r = left.getAnnotation(Option.class).category().compareTo( + right.getAnnotation(Option.class).category()); + return r == 0 ? BY_NAME.compare(left, right) : r; + } + }; + + private static String getTypeDescription(Field optionsField) { + return findConverter(optionsField).getTypeDescription(); + } + + static String getFlagName(Field field) { + String name = field.getAnnotation(Option.class).name(); + return OptionsParserImpl.isBooleanField(field) ? "[no]" + name : name; + } + +} |