/* * Copyright 2006 Tim Wood * * 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.lexicalscope.jewel.cli; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import com.lexicalscope.fluent.FluentDollar; import com.lexicalscope.fluent.list.FluentList; import com.lexicalscope.fluentreflection.FluentClass; import com.lexicalscope.fluentreflection.FluentMethod; import com.lexicalscope.jewel.cli.specification.CliSpecification; import com.lexicalscope.jewel.cli.specification.OptionsSpecification; import com.lexicalscope.jewel.cli.specification.ParsedOptionSpecification; import com.lexicalscope.jewel.cli.specification.UnparsedOptionSpecification; class OptionsSpecificationImpl<O> implements OptionsSpecification<O>, CliSpecification { private final FluentClass<O> klass; private final Set<ParsedOptionSpecification> options; private final Map<String, ParsedOptionSpecification> optionsByName = new TreeMap<String, ParsedOptionSpecification>(); private final Map<FluentMethod, ParsedOptionSpecification> optionsMethod = new HashMap<FluentMethod, ParsedOptionSpecification>(); private final Map<FluentMethod, ParsedOptionSpecification> optionalOptionsMethod = new HashMap<FluentMethod, ParsedOptionSpecification>(); private final Map<FluentMethod, UnparsedOptionSpecification> unparsedOptionsMethod = new HashMap<FluentMethod, UnparsedOptionSpecification>(); private final Map<FluentMethod, UnparsedOptionSpecification> unparsedOptionalOptionsMethod = new HashMap<FluentMethod, UnparsedOptionSpecification>(); OptionsSpecificationImpl( final FluentClass<O> klass, final List<ParsedOptionSpecification> optionSpecifications, final List<UnparsedOptionSpecification> unparsedSpecifications) { this.klass = klass; switch (optionOrdering(klass)) { case LONGNAME: options = new TreeSet<ParsedOptionSpecification>(new Comparator<ParsedOptionSpecification>() { @Override public int compare(final ParsedOptionSpecification o1, final ParsedOptionSpecification o2) { final String o1Name = o1.getLongName().get(0); final String o2Name = o2.getLongName().get(0); return o1Name.compareTo(o2Name); } }); break; case LEXICOGRAPHIC: options = new TreeSet<ParsedOptionSpecification>(); break; default: options = new LinkedHashSet<ParsedOptionSpecification>(); break; } for (final ParsedOptionSpecification optionSpecification : optionSpecifications) { addOption(optionSpecification); } for (final UnparsedOptionSpecification optionSpecification : unparsedSpecifications) { addUnparsedOption(optionSpecification); } } private OptionOrder optionOrdering(final FluentClass<O> klass) { if (klass.annotatedWith(CommandLineInterface.class)) { return klass.annotation(CommandLineInterface.class).order(); } return OptionOrder.LEXICOGRAPHIC; } @Override public boolean isSpecified(final String key) { return optionsByName.containsKey(key); } @Override public ParsedOptionSpecification getSpecification(final String key) { return optionsByName.get(key); } @Override public ParsedOptionSpecification getSpecification(final FluentMethod method) { if (optionsMethod.containsKey(method)) { return optionsMethod.get(method); } return optionalOptionsMethod.get(method); } @Override public FluentList<ParsedOptionSpecification> getMandatoryOptions() { final FluentList<ParsedOptionSpecification> result = FluentDollar.$.list(ParsedOptionSpecification.class); for (final ParsedOptionSpecification specification : options) { if (!specification.isOptional() && !specification.hasDefaultValue()) { result.add(specification); } } return result; } @Override public Iterator<ParsedOptionSpecification> iterator() { return new ArrayList<ParsedOptionSpecification>(optionsByName.values()).iterator(); } @Override public UnparsedOptionSpecification getUnparsedSpecification() { return unparsedOptionsMethod.values().iterator().next(); } @Override public boolean hasUnparsedSpecification() { return !unparsedOptionsMethod.values().isEmpty(); } @Override public String getApplicationName() { final String applicationName = applicationName(); if (applicationName != null) { return applicationName; } return klass.name(); } private String applicationName() { if (klass.annotatedWith(CommandLineInterface.class)) { final String applicationName = klass.annotation(CommandLineInterface.class).application(); if (applicationName != null && !applicationName.trim().equals("")) { return applicationName.trim(); } } return null; } private void addOption(final ParsedOptionSpecification optionSpecification) { for (final String name : optionSpecification.getNames()) { optionsByName.put(name, optionSpecification); } options.add(optionSpecification); optionsMethod.put(optionSpecification.getMethod(), optionSpecification); if (optionSpecification.isOptional()) { optionalOptionsMethod.put(optionSpecification.getOptionalityMethod(), optionSpecification); } } private void addUnparsedOption(final UnparsedOptionSpecification optionSpecification) { unparsedOptionsMethod.put(optionSpecification.getMethod(), optionSpecification); if (optionSpecification.isOptional()) { unparsedOptionalOptionsMethod.put(optionSpecification.getOptionalityMethod(), optionSpecification); } } @Override public void describeTo(final HelpMessage helpMessage) { if (!hasCustomApplicationName() && (!hasUnparsedSpecification() || getUnparsedSpecification().isHidden())) { helpMessage.noUsageInformation(); } else { if (hasCustomApplicationName()) { helpMessage.hasUsageInformation(applicationName()); } else { helpMessage.hasUsageInformation(); } if (getMandatoryOptions().isEmpty()) { helpMessage.hasOnlyOptionalOptions(); } else { helpMessage.hasSomeMandatoryOptions(); } if (hasUnparsedSpecification() && !getUnparsedSpecification().isHidden()) { if (getUnparsedSpecification().isMultiValued()) { helpMessage.hasUnparsedMultiValuedOption(getUnparsedSpecification().getValueName()); } else { helpMessage.hasUnparsedOption(getUnparsedSpecification().getValueName()); } } } helpMessage.startOfOptions(); for (final ParsedOptionSpecification specification : options) { if (!specification.isHidden()) { new ParsedOptionSummary(specification).describeOptionTo(helpMessage.option()); } } helpMessage.endOfOptions(); } private boolean hasCustomApplicationName() { return !nullOrBlank(applicationName()); } static boolean nullOrBlank(final String string) { return string == null || string.trim().equals(""); } @Override public String toString() { final HelpMessageBuilderImpl helpMessage = new HelpMessageBuilderImpl(); describeTo(helpMessage); return helpMessage.toString(); } }