| /* |
| * Copyright 2014 Google Inc. |
| * |
| * 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.gwt.util.tools; |
| |
| import static com.google.gwt.thirdparty.guava.common.base.Preconditions.checkNotNull; |
| |
| import com.google.gwt.thirdparty.guava.common.base.Ascii; |
| import com.google.gwt.thirdparty.guava.common.base.Enums; |
| import com.google.gwt.thirdparty.guava.common.base.Joiner; |
| import com.google.gwt.thirdparty.guava.common.base.Preconditions; |
| import com.google.gwt.thirdparty.guava.common.base.Predicate; |
| import com.google.gwt.thirdparty.guava.common.collect.FluentIterable; |
| |
| import java.util.Arrays; |
| import java.util.List; |
| |
| import javax.annotation.Nullable; |
| |
| /** |
| * A generic arg handler for options defined by enums. |
| * |
| * @param <T> enum type providing option values. |
| */ |
| public abstract class ArgHandlerEnum<T extends Enum<T>> extends ArgHandler { |
| private final Class<T> optionsEnumClass; |
| private final T defaultValue; |
| private final boolean allowAbbreviation; |
| |
| private static final int ABBREVIATION_MIN_SIZE = 3; |
| |
| /** |
| * Constructor, default value must be handled by the user code. |
| */ |
| public ArgHandlerEnum(Class<T> optionsEnumClass) { |
| this(optionsEnumClass, null, false); |
| } |
| |
| /** |
| * Constructor, allows to specify the default value for the option and whether to accept or |
| * prefixes as abbreviations. |
| * <p> |
| * When {@code defaultValue} is null, handling of the default for the option is left to be |
| * handled by the user code. |
| */ |
| public ArgHandlerEnum( |
| Class<T> optionsEnumClass, @Nullable T defaultValue, boolean allowAbbreviation) { |
| Preconditions.checkArgument(optionsEnumClass.getEnumConstants().length > 1); |
| this.optionsEnumClass = checkNotNull(optionsEnumClass); |
| this.defaultValue = defaultValue; |
| this.allowAbbreviation = allowAbbreviation; |
| } |
| |
| @Override |
| public String[] getDefaultArgs() { |
| if (defaultValue == null) { |
| return null; |
| } |
| return new String[] { getTag(), defaultValue.name() }; |
| } |
| |
| @Override |
| public String[] getTagArgs() { |
| return new String[]{ "(" + getFormattedOptionNames("|", optionsEnumClass) + ")" }; |
| } |
| |
| @Override |
| public final int handle(String[] args, int startIndex) { |
| // A command line argument corresponding to an enum option has a parameter |
| // (the desired value), will be at startIndex + 1. |
| int optionValueIndex = startIndex + 1; |
| if (optionValueIndex < args.length) { |
| String value = Ascii.toUpperCase(args[optionValueIndex].trim()); |
| T mode = matchOption(value); |
| if (mode == null) { |
| System.err.format("%s is not a valid option for %s\n", value, getTag()); |
| System.err.format("%s value must be one of %s.\n", |
| getTag(), getFormattedOptionNames(", ", " or ", optionsEnumClass)); |
| return -1; |
| } |
| setValue(mode); |
| return 1; |
| } |
| |
| System.err.format("Missing argument for %s must be followed by one of %s.\n" + |
| getTag(), getFormattedOptionNames(", ", " or ", optionsEnumClass)); |
| return -1; |
| } |
| |
| protected String getPurposeString(String prefix) { |
| String maybeExperimentalString = isExperimental() ? "EXPERIMENTAL: " : ""; |
| String defaultValueString = defaultValue != null ? |
| " (defaults to " + defaultValue.name() + ")" : ""; |
| |
| return String.format("%s%s %s%s", maybeExperimentalString, prefix, |
| getFormattedOptionNames(", ", " or ", optionsEnumClass), defaultValueString); |
| } |
| |
| /** |
| * Override to handle the setting of an enum value. |
| */ |
| public abstract void setValue(T value); |
| |
| private List<String> getEnumNames(Class<T> optionsEnumClass) { |
| return FluentIterable.from(Arrays.asList(optionsEnumClass.getEnumConstants())) |
| .transform(Enums.stringConverter(optionsEnumClass).reverse()) |
| .toList(); |
| } |
| |
| private String getFormattedOptionNames(String separator, Class<T> optionsEnumClass) { |
| return getFormattedOptionNames(separator, separator, optionsEnumClass); |
| } |
| |
| private String getFormattedOptionNames( |
| String separator, String lastSeparator, Class<T> optionsEnumClass) { |
| List<String> enumNames = getEnumNames(optionsEnumClass); |
| List<String> allNamesButLast = enumNames.subList(0, enumNames.size() - 1); |
| String lastName = enumNames.get(enumNames.size() - 1); |
| |
| return Joiner.on(separator).join(allNamesButLast) + lastSeparator + lastName; |
| } |
| |
| private Predicate<Enum<?>> buildMatchPredicate(final String value) { |
| return |
| new Predicate<Enum<?>>() { |
| @Override |
| public boolean apply(Enum<?> t) { |
| if (allowAbbreviation && value.length() >= ABBREVIATION_MIN_SIZE) { |
| return t.name().startsWith(value); |
| } |
| return t.name().equals(value); |
| } |
| }; |
| } |
| |
| private T matchOption(String value) { |
| List<T> matchedOptions = FluentIterable.from(Arrays.asList(optionsEnumClass.getEnumConstants())) |
| .filter(buildMatchPredicate(value)) |
| .toList(); |
| return matchedOptions.size() == 1 ? matchedOptions.get(0) : null; |
| } |
| } |