aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/common/options/OptionsUsage.java
diff options
context:
space:
mode:
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.java156
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;
+ }
+
+}