pax_global_header00006660000000000000000000000064124267417430014524gustar00rootroot0000000000000052 comment=da7e1eea7ffb1d08451250f5c57d1ca5748bd1a9 airline-0.7/000077500000000000000000000000001242674174300130155ustar00rootroot00000000000000airline-0.7/.gitignore000066400000000000000000000003121242674174300150010ustar00rootroot00000000000000*.iml *.ipr *.iws /target pom.xml.versionsBackup /test-output atlassian-ide-plugin.xml .idea .DS_Store .classpath .project .settings .project temp-testng-customsuite.xml .externalToolBuilders *~ .rvmrc airline-0.7/.travis.yml000066400000000000000000000000171242674174300151240ustar00rootroot00000000000000language: java airline-0.7/README.md000066400000000000000000000153431242674174300143020ustar00rootroot00000000000000Airline ======= Airline is a Java annotation-based framework for parsing Git like command line structures. Latest release is 0.6, available from Maven Central. ```xml io.airlift airline 0.6 ``` Here is a quick example: ```java public class Git { public static void main(String[] args) { CliBuilder builder = Cli.builder("git") .withDescription("the stupid content tracker") .withDefaultCommand(Help.class) .withCommands(Help.class, Add.class); builder.withGroup("remote") .withDescription("Manage set of tracked repositories") .withDefaultCommand(RemoteShow.class) .withCommands(RemoteShow.class, RemoteAdd.class); Cli gitParser = builder.build(); gitParser.parse(args).run(); } public static class GitCommand implements Runnable { @Option(type = OptionType.GLOBAL, name = "-v", description = "Verbose mode") public boolean verbose; public void run() { System.out.println(getClass().getSimpleName()); } } @Command(name = "add", description = "Add file contents to the index") public static class Add extends GitCommand { @Arguments(description = "Patterns of files to be added") public List patterns; @Option(name = "-i", description = "Add modified contents interactively.") public boolean interactive; } @Command(name = "show", description = "Gives some information about the remote ") public static class RemoteShow extends GitCommand { @Option(name = "-n", description = "Do not query remote heads") public boolean noQuery; @Arguments(description = "Remote to show") public String remote; } @Command(name = "add", description = "Adds a remote") public static class RemoteAdd extends GitCommand { @Option(name = "-t", description = "Track only a specific branch") public String branch; @Arguments(description = "Remote repository to add") public List remote; } } ``` Assuming you have packaged this as an executable program named 'git', you would be able to execute the following commands: ```shell $ git add -p file $ git remote add origin git@github.com:airlift/airline.git $ git -v remote show origin ``` Single Command Mode =================== Airline can also be used for simple, single-command programs: ```java @Command(name = "ping", description = "network test utility") public class Ping { @Inject public HelpOption helpOption; @Option(name = {"-c", "--count"}, description = "Send count packets") public int count = 1; public static void main(String... args) { Ping ping = SingleCommand.singleCommand(Ping.class).parse(args); if (ping.helpOption.showHelpIfRequested()) { return; } ping.run(); } public void run() { System.out.println("Ping count: " + count); } } ``` Assuming you have packaged this as an executable program named 'ping', you would be able to execute the following commands: ```shell $ ping $ ping -c 5 $ ping --help ``` Help System =========== Airline contains a fully automated help system, which generates man-page-like documentation driven by the Java annotations. As you may have noticed in the git code above, we added `Help.class` to the cli. This command is provided by Airline and works as follows: ```shell $ git help usage: git [-v] [] The most commonly used git commands are: add Add file contents to the index help Display help information remote Manage set of tracked repositories See 'git help ' for more information on a specific command. $ git help git NAME git - the stupid content tracker SYNOPSIS git [-v] [] OPTIONS -v Verbose mode COMMANDS help Display help information add Add file contents to the index remote show Gives some information about the remote remote add Adds a remote $ git help add NAME git add - Add file contents to the index SYNOPSIS git [-v] add [-i] [--] [...] OPTIONS -i Add modified contents interactively. -v Verbose mode -- This option can be used to separate command-line options from the list of argument, (useful when arguments might be mistaken for command-line options Patterns of files to be added $ git help remote NAME git remote - Manage set of tracked repositories SYNOPSIS git [-v] remote git [-v] remote add [-t ] git [-v] remote show [-n] OPTIONS -v Verbose mode COMMANDS With no arguments, Gives some information about the remote show Gives some information about the remote With -n option, Do not query remote heads add Adds a remote With -t option, Track only a specific branch $ git help remote show NAME git remote show - Gives some information about the remote SYNOPSIS git [-v] remote show [-n] [--] [] OPTIONS -n Do not query remote heads -v Verbose mode -- This option can be used to separate command-line options from the list of argument, (useful when arguments might be mistaken for command-line options Remote to show ``` We have also, add `Help.class` as the default command for git, so if you execute git without any arguments, you will see the following: ```shell $ git help usage: git [-v] [] The most commonly used git commands are: add Add file contents to the index help Display help information remote Manage set of tracked repositories See 'git help ' for more information on a specific command. ``` For simple, single-command programs like ping, use the `HelpOption` option as shown in the example above. `HelpOption` handles the options `-h` and `--help` and provides the `showHelpIfRequested()` method to automatically show the following help output: ```shell $ ping -h NAME ping - network test utility SYNOPSIS ping [(-c | --count )] [(-h | --help)] OPTIONS -c , --count Send count packets -h, --help Display help information ``` airline-0.7/license.txt000066400000000000000000000261371242674174300152110ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. airline-0.7/notice.md000066400000000000000000000002031242674174300146130ustar00rootroot00000000000000Copyright Notices ================= Copyright 2011 Dain Sundstrom Copyright 2010 Cedric Beust airline-0.7/pom.xml000066400000000000000000000047101242674174300143340ustar00rootroot00000000000000 4.0.0 io.airlift airbase 28 airline jar 0.7 airline Java annotation-based framework for parsing Git like command line structures https://github.com/airlift/airline 2012 scm:git:git@github.com:airlift/airline.git http://github.com/airlift/airline/tree/master 0.7 true true true javax.inject javax.inject com.google.code.findbugs annotations com.google.guava guava org.testng testng test airline-0.7/src/000077500000000000000000000000001242674174300136045ustar00rootroot00000000000000airline-0.7/src/main/000077500000000000000000000000001242674174300145305ustar00rootroot00000000000000airline-0.7/src/main/java/000077500000000000000000000000001242674174300154515ustar00rootroot00000000000000airline-0.7/src/main/java/io/000077500000000000000000000000001242674174300160605ustar00rootroot00000000000000airline-0.7/src/main/java/io/airlift/000077500000000000000000000000001242674174300175125ustar00rootroot00000000000000airline-0.7/src/main/java/io/airlift/airline/000077500000000000000000000000001242674174300211355ustar00rootroot00000000000000airline-0.7/src/main/java/io/airlift/airline/Accessor.java000066400000000000000000000206141242674174300235450ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; public class Accessor { private final String name; private final Class javaType; private final List path; private boolean multiValued; public Accessor(Field... path) { this(ImmutableList.copyOf(path)); } public Accessor(Iterable path) { Preconditions.checkNotNull(path, "path is null"); Preconditions.checkArgument(!Iterables.isEmpty(path), "path is empty"); this.path = ImmutableList.copyOf(path); this.name = this.path.get(0).getDeclaringClass().getSimpleName() + "." + Joiner.on('.').join(Iterables.transform(this.path, new Function() { public String apply(Field field) { return field.getName(); } })); Field field = this.path.get(this.path.size() - 1); multiValued = Collection.class.isAssignableFrom(field.getType()); javaType = getItemType(name, field.getGenericType()); } public String getName() { return name; } public Class getJavaType() { return javaType; } public boolean isMultiValued() { return multiValued; } public Object getValue(Object instance) { StringBuilder pathName = new StringBuilder(); for (Field intermediateField : path.subList(0, path.size() - 1)) { if (pathName.length() != 0) { pathName.append("."); } pathName.append(intermediateField.getName()); try { Object nextInstance = intermediateField.get(instance); if (nextInstance == null) { nextInstance = ParserUtil.createInstance(intermediateField.getType()); intermediateField.set(instance, nextInstance); } instance = nextInstance; } catch (Exception e) { throw new ParseException(e, "Error getting value of %s", pathName); } } return instance; } public void addValues(Object commandInstance, Iterable values) { if (Iterables.isEmpty(values)) { return; } // get the actual instance Object instance = getValue(commandInstance); Field field = path.get(path.size() - 1); field.setAccessible(true); if (Collection.class.isAssignableFrom(field.getType())) { Collection collection = getOrCreateCollectionField(name, instance, field); Iterables.addAll(collection, values); } else { try { field.set(instance, Iterables.getLast(values)); } catch (Exception e) { throw new ParseException(e, "Error setting %s for argument %s", field.getName(), name); } } } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } Accessor accessor = (Accessor) o; if (!path.equals(accessor.path)) { return false; } return true; } @Override public int hashCode() { return path.hashCode(); } @Override public String toString() { return name; } // // Private reflection helper methods // @SuppressWarnings("unchecked") private static Collection newCollection(Class type) { if (Collection.class.equals(type) || List.class.equals(type)) { return new ArrayList(); } if (Set.class.equals(type)) { return new HashSet(); } if (SortedSet.class.equals(type)) { return new TreeSet(); } try { return (Collection) type.getConstructor().newInstance(); } catch (Exception ignored) { } throw new ParseException("Parameters of Collection type '%s' are not supported. Please use List or Set instead.", type.getSimpleName()); } private static Collection getOrCreateCollectionField(String name, Object object, Field field) { Collection collection; try { collection = (Collection) field.get(object); } catch (Exception e) { throw new ParseException(e, "Error getting collection field %s for argument %s", field.getName(), name); } if (collection == null) { collection = newCollection(field.getType()); try { field.set(object, collection); } catch (Exception e) { throw new ParseException(e, "Error setting collection field %s for argument %s", field.getName(), name); } } return collection; } private static Class getItemType(String name, Type type) { Class rawClass = getRawType(type); if (rawClass == null) { throw new ParseException("Type of option %s be an exact type", name); } if (!Collection.class.isAssignableFrom(rawClass)) { return rawClass; } Type[] types = getTypeParameters(Collection.class, type); if ((types == null) || (types.length != 1)) { throw new ParseException("Unable to get item type of Collection option %s", name); } Type itemType = types[0]; if (!(itemType instanceof Class)) { throw new ParseException("Collection type option %s must be an exact type", name); } return (Class) itemType; } private static Class getRawType(Type type) { if (type instanceof Class) { return (Class) type; } if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; return getRawType(parameterizedType.getRawType()); } return null; } private static Type[] getTypeParameters(Class desiredType, Type type) { if (type instanceof Class) { Class rawClass = (Class) type; // if this is the collection class we're done if (desiredType.equals(type)) { return null; } for (Type iface : rawClass.getGenericInterfaces()) { Type[] collectionType = getTypeParameters(desiredType, iface); if (collectionType != null) { return collectionType; } } return getTypeParameters(desiredType, rawClass.getGenericSuperclass()); } if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; Type rawType = parameterizedType.getRawType(); if (desiredType.equals(rawType)) { return parameterizedType.getActualTypeArguments(); } Type[] collectionTypes = getTypeParameters(desiredType, rawType); if (collectionTypes != null) { for (int i = 0; i < collectionTypes.length; i++) { if (collectionTypes[i] instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) collectionTypes[i]; TypeVariable[] rawTypeParams = ((Class) rawType).getTypeParameters(); for (int j = 0; j < rawTypeParams.length; j++) { if (typeVariable.getName().equals(rawTypeParams[j].getName())) { collectionTypes[i] = parameterizedType.getActualTypeArguments()[j]; } } } } } return collectionTypes; } return null; } } airline-0.7/src/main/java/io/airlift/airline/Arguments.java000066400000000000000000000025061242674174300237500ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Target({FIELD}) public @interface Arguments { /** * Name of the arguments. */ String title() default ""; /** * A description of the arguments. */ String description() default ""; /** * Argument usage for help. */ String usage() default ""; /** * Whether this arguments are required. */ boolean required() default false; } airline-0.7/src/main/java/io/airlift/airline/Cli.java000066400000000000000000000246011242674174300225120ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.MetadataLoader; import io.airlift.airline.model.OptionMetadata; import java.util.List; import java.util.Map; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; import static io.airlift.airline.ParserUtil.createInstance; public class Cli { public static CliBuilder builder(String name) { Preconditions.checkNotNull(name, "name is null"); return new CliBuilder(name); } @Deprecated public static CliBuilder buildCli(String name) { return builder(name); } @Deprecated public static CliBuilder buildCli(String name, Class commandTypes) { return builder(name); } private final GlobalMetadata metadata; private Cli(String name, String description, TypeConverter typeConverter, Class defaultCommand, Iterable> defaultGroupCommands, Iterable> groups) { Preconditions.checkNotNull(name, "name is null"); Preconditions.checkNotNull(typeConverter, "typeConverter is null"); CommandMetadata defaultCommandMetadata = null; if (defaultCommand != null) { defaultCommandMetadata = MetadataLoader.loadCommand(defaultCommand); } List defaultCommandGroup = MetadataLoader.loadCommands(defaultGroupCommands); List commandGroups = ImmutableList.copyOf(Iterables.transform(groups, new Function, CommandGroupMetadata>() { public CommandGroupMetadata apply(GroupBuilder group) { return MetadataLoader.loadCommandGroup(group.name, group.description, MetadataLoader.loadCommand(group.defaultCommand), MetadataLoader.loadCommands(group.commands)); } })); this.metadata = MetadataLoader.loadGlobal(name, description, defaultCommandMetadata, defaultCommandGroup, commandGroups); } public GlobalMetadata getMetadata() { return metadata; } public C parse(String... args) { return parse(ImmutableList.copyOf(args)); } public C parse(Iterable args) { Preconditions.checkNotNull(args, "args is null"); Parser parser = new Parser(); ParseState state = parser.parse(metadata, args); if (state.getCommand() == null) { if (state.getGroup() != null) { state = state.withCommand(state.getGroup().getDefaultCommand()); } else { state = state.withCommand(metadata.getDefaultCommand()); } } validate(state); CommandMetadata command = state.getCommand(); return createInstance(command.getType(), command.getAllOptions(), state.getParsedOptions(), command.getArguments(), state.getParsedArguments(), command.getMetadataInjections(), ImmutableMap., Object>of(GlobalMetadata.class, metadata)); } private void validate(ParseState state) { CommandMetadata command = state.getCommand(); if (command == null) { List unparsedInput = state.getUnparsedInput(); if (unparsedInput.isEmpty()) { throw new ParseCommandMissingException(); } else { throw new ParseCommandUnrecognizedException(unparsedInput); } } ArgumentsMetadata arguments = command.getArguments(); if (state.getParsedArguments().isEmpty() && arguments != null && arguments.isRequired()) { throw new ParseArgumentsMissingException(arguments.getTitle()); } if (!state.getUnparsedInput().isEmpty()) { throw new ParseArgumentsUnexpectedException(state.getUnparsedInput()); } if (state.getLocation() == Context.OPTION) { throw new ParseOptionMissingValueException(state.getCurrentOption().getTitle()); } for (OptionMetadata option : command.getAllOptions()) { if (option.isRequired() && !state.getParsedOptions().containsKey(option)) { throw new ParseOptionMissingException(option.getOptions().iterator().next()); } } } // // Builder Classes // public static class CliBuilder { protected final String name; protected String description; protected TypeConverter typeConverter = new TypeConverter(); protected String optionSeparators; private Class defaultCommand; private final List> defaultCommandGroupCommands = newArrayList(); protected final Map> groups = newHashMap(); public CliBuilder(String name) { Preconditions.checkNotNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); this.name = name; } public CliBuilder withDescription(String description) { Preconditions.checkNotNull(description, "description is null"); Preconditions.checkArgument(!description.isEmpty(), "description is empty"); this.description = description; return this; } // public CliBuilder withTypeConverter(TypeConverter typeConverter) // { // Preconditions.checkNotNull(typeConverter, "typeConverter is null"); // this.typeConverter = typeConverter; // return this; // } // public CliBuilder withOptionSeparators(String optionsSeparator) // { // Preconditions.checkNotNull(optionsSeparator, "optionsSeparator is null"); // this.optionSeparators = optionsSeparator; // return this; // } public CliBuilder withDefaultCommand(Class defaultCommand) { this.defaultCommand = defaultCommand; return this; } public CliBuilder withCommand(Class command) { this.defaultCommandGroupCommands.add(command); return this; } public CliBuilder withCommands(Class command, Class... moreCommands) { this.defaultCommandGroupCommands.add(command); this.defaultCommandGroupCommands.addAll(ImmutableList.copyOf(moreCommands)); return this; } public CliBuilder withCommands(Iterable> commands) { this.defaultCommandGroupCommands.addAll(ImmutableList.copyOf(commands)); return this; } public GroupBuilder withGroup(String name) { Preconditions.checkNotNull(name, "name is null"); Preconditions.checkArgument(!name.isEmpty(), "name is empty"); if (groups.containsKey(name)) { return groups.get(name); } GroupBuilder group = new GroupBuilder(name); groups.put(name, group); return group; } public Cli build() { return new Cli(name, description, typeConverter, defaultCommand, defaultCommandGroupCommands, groups.values()); } } public static class GroupBuilder { private final String name; private String description = null; private Class defaultCommand = null; private final List> commands = newArrayList(); private GroupBuilder(String name) { Preconditions.checkNotNull(name, "name is null"); this.name = name; } public GroupBuilder withDescription(String description) { Preconditions.checkNotNull(description, "description is null"); Preconditions.checkArgument(!description.isEmpty(), "description is empty"); Preconditions.checkState(this.description == null, "description is already set"); this.description = description; return this; } public GroupBuilder withDefaultCommand(Class defaultCommand) { Preconditions.checkNotNull(defaultCommand, "defaultCommand is null"); Preconditions.checkState(this.defaultCommand == null, "defaultCommand is already set"); this.defaultCommand = defaultCommand; return this; } public GroupBuilder withCommand(Class command) { Preconditions.checkNotNull(command, "command is null"); commands.add(command); return this; } public GroupBuilder withCommands(Class command, Class... moreCommands) { this.commands.add(command); this.commands.addAll(ImmutableList.copyOf(moreCommands)); return this; } public GroupBuilder withCommands(Iterable> commands) { this.commands.addAll(ImmutableList.copyOf(commands)); return this; } } } airline-0.7/src/main/java/io/airlift/airline/Command.java000066400000000000000000000026351242674174300233640ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** * Marks a class as a command. */ @Target(TYPE) @Retention(RUNTIME) @Documented public @interface Command { /** * Name of the command. Command name is split on white space to form a multi-word name. */ String name(); /** * Description of the command. */ String description() default ""; /** * If true, this command won't appear in the usage(). */ boolean hidden() default false; } airline-0.7/src/main/java/io/airlift/airline/CommandGroupUsage.java000066400000000000000000000151221242674174300253610ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Preconditions; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.OptionMetadata; import javax.annotation.Nullable; import java.util.Collections; import java.util.Comparator; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.UsageHelper.DEFAULT_COMMAND_COMPARATOR; import static io.airlift.airline.UsageHelper.DEFAULT_OPTION_COMPARATOR; public class CommandGroupUsage { private final int columnSize; private final boolean hideGlobalOptions; private final Comparator optionComparator; private final Comparator commandComparator = DEFAULT_COMMAND_COMPARATOR; public CommandGroupUsage() { this(79, false, DEFAULT_OPTION_COMPARATOR); } public CommandGroupUsage(int columnSize) { this(columnSize, false, DEFAULT_OPTION_COMPARATOR); } public CommandGroupUsage(int columnSize, boolean hideGlobalOptions) { this(columnSize, hideGlobalOptions, DEFAULT_OPTION_COMPARATOR); } public CommandGroupUsage(int columnSize, boolean hideGlobalOptions, @Nullable Comparator optionComparator) { Preconditions.checkArgument(columnSize > 0, "columnSize must be greater than 0"); this.columnSize = columnSize; this.hideGlobalOptions = hideGlobalOptions; this.optionComparator = optionComparator; } /** * Display the help on System.out. */ public void usage(@Nullable GlobalMetadata global, CommandGroupMetadata group) { StringBuilder stringBuilder = new StringBuilder(); usage(global, group, stringBuilder); System.out.println(stringBuilder.toString()); } /** * Store the help in the passed string builder. */ public void usage(@Nullable GlobalMetadata global, CommandGroupMetadata group, StringBuilder out) { usage(global, group, new UsagePrinter(out, columnSize)); } public void usage(@Nullable GlobalMetadata global, CommandGroupMetadata group, UsagePrinter out) { // // NAME // out.append("NAME").newline(); out.newIndentedPrinter(8) .append(global.getName()) .append(group.getName()) .append("-") .append(group.getDescription()) .newline() .newline(); // // SYNOPSIS // out.append("SYNOPSIS").newline(); UsagePrinter synopsis = out.newIndentedPrinter(8).newPrinterWithHangingIndent(8); List commands = newArrayList(group.getCommands()); Collections.sort(commands, commandComparator); if (group.getDefaultCommand() != null) { CommandMetadata command = group.getDefaultCommand(); if (global != null) { synopsis.append(global.getName()); if (!hideGlobalOptions) { synopsis.appendWords(UsageHelper.toSynopsisUsage(command.getGlobalOptions())); } } synopsis.append(group.getName()).appendWords(UsageHelper.toSynopsisUsage(command.getGroupOptions())); synopsis.newline(); } for (CommandMetadata command : commands) { if (global != null) { synopsis.append(global.getName()); if (!hideGlobalOptions) { synopsis.appendWords(UsageHelper.toSynopsisUsage(command.getGlobalOptions())); } } synopsis.append(group.getName()).appendWords(UsageHelper.toSynopsisUsage(command.getGroupOptions())); synopsis.append(command.getName()).appendWords(UsageHelper.toSynopsisUsage(command.getCommandOptions())); synopsis.newline(); } synopsis.newline(); // // OPTIONS // List options = newArrayList(); options.addAll(group.getOptions()); if (global != null && !hideGlobalOptions) { options.addAll(global.getOptions()); } if (options.size() > 0) { if (optionComparator != null) { Collections.sort(options, optionComparator); } out.append("OPTIONS").newline(); for (OptionMetadata option : options) { // option names UsagePrinter optionPrinter = out.newIndentedPrinter(8); optionPrinter.append(UsageHelper.toDescription(option)).newline(); // description UsagePrinter descriptionPrinter = optionPrinter.newIndentedPrinter(4); descriptionPrinter.append(option.getDescription()).newline(); descriptionPrinter.newline(); } } // // COMMANDS // if (commands.size() > 0 || group.getDefaultCommand() != null) { out.append("COMMANDS").newline(); UsagePrinter commandPrinter = out.newIndentedPrinter(8); if (group.getDefaultCommand() != null && group.getDefaultCommand().getDescription() != null) { commandPrinter.append("With no arguments,") .append(group.getDefaultCommand().getDescription()) .newline() .newline(); } for (CommandMetadata command : group.getCommands()) { commandPrinter.append(command.getName()).newline(); UsagePrinter descriptionPrinter = commandPrinter.newIndentedPrinter(4); descriptionPrinter.append(command.getDescription()).newline().newline(); for (OptionMetadata option : command.getCommandOptions()) { if (!option.isHidden() && option.getDescription() != null) { descriptionPrinter.append("With") .append(longest(option.getOptions())) .append("option,") .append(option.getDescription()) .newline() .newline(); } } } } } private static String longest(Iterable iterable) { String longest = ""; for (String value : iterable) { if (value.length() > longest.length()) { longest = value; } } return longest; } } airline-0.7/src/main/java/io/airlift/airline/CommandSuggester.java000066400000000000000000000014621242674174300252520ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.collect.ImmutableList; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.OptionMetadata; import javax.inject.Inject; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.transform; public class CommandSuggester implements Suggester { @Inject public CommandMetadata command; @Override public Iterable suggest() { ImmutableList.Builder suggestions = ImmutableList.builder() .addAll(concat(transform(command.getCommandOptions(), OptionMetadata.optionsGetter()))); if (command.getArguments() != null) { suggestions.add("--"); } return suggestions.build(); } } airline-0.7/src/main/java/io/airlift/airline/CommandUsage.java000066400000000000000000000125751242674174300243550ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Preconditions; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.OptionMetadata; import javax.annotation.Nullable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.UsageHelper.DEFAULT_OPTION_COMPARATOR; import static io.airlift.airline.UsageHelper.toSynopsisUsage; public class CommandUsage { private final int columnSize; private final Comparator optionComparator; public CommandUsage() { this(79, DEFAULT_OPTION_COMPARATOR); } public CommandUsage(int columnSize) { this(columnSize, DEFAULT_OPTION_COMPARATOR); } public CommandUsage(int columnSize, @Nullable Comparator optionComparator) { Preconditions.checkArgument(columnSize > 0, "columnSize must be greater than 0"); this.columnSize = columnSize; this.optionComparator = optionComparator; } /** * Display the help on System.out. */ public void usage(@Nullable String programName, @Nullable String groupName, String commandName, CommandMetadata command) { StringBuilder stringBuilder = new StringBuilder(); usage(programName, groupName, commandName, command, stringBuilder); System.out.println(stringBuilder.toString()); } /** * Store the help in the passed string builder. */ public void usage(@Nullable String programName, @Nullable String groupName, String commandName, CommandMetadata command, StringBuilder out) { usage(programName, groupName, commandName, command, new UsagePrinter(out, columnSize)); } public void usage(@Nullable String programName, @Nullable String groupName, String commandName, CommandMetadata command, UsagePrinter out) { // // NAME // out.append("NAME").newline(); out.newIndentedPrinter(8) .append(programName) .append(groupName) .append(commandName) .append("-") .append(command.getDescription()) .newline() .newline(); // // SYNOPSIS // out.append("SYNOPSIS").newline(); UsagePrinter synopsis = out.newIndentedPrinter(8).newPrinterWithHangingIndent(8); List options = newArrayList(); if (programName != null) { synopsis.append(programName).appendWords(toSynopsisUsage(sortOptions(command.getGlobalOptions()))); options.addAll(command.getGlobalOptions()); } if (groupName != null) { synopsis.append(groupName).appendWords(toSynopsisUsage(sortOptions(command.getGroupOptions()))); options.addAll(command.getGroupOptions()); } synopsis.append(commandName).appendWords(toSynopsisUsage(sortOptions(command.getCommandOptions()))); options.addAll(command.getCommandOptions()); // command arguments (optional) ArgumentsMetadata arguments = command.getArguments(); if (arguments != null) { synopsis.append("[--]") .append(UsageHelper.toUsage(arguments)); } synopsis.newline(); synopsis.newline(); // // OPTIONS // if (options.size() > 0 || arguments != null) { options = sortOptions(options); out.append("OPTIONS").newline(); for (OptionMetadata option : options) { // skip hidden options if (option.isHidden()) { continue; } // option names UsagePrinter optionPrinter = out.newIndentedPrinter(8); optionPrinter.append(UsageHelper.toDescription(option)).newline(); // description UsagePrinter descriptionPrinter = optionPrinter.newIndentedPrinter(4); descriptionPrinter.append(option.getDescription()).newline(); descriptionPrinter.newline(); } if (arguments != null) { // "--" option UsagePrinter optionPrinter = out.newIndentedPrinter(8); optionPrinter.append("--").newline(); // description UsagePrinter descriptionPrinter = optionPrinter.newIndentedPrinter(4); descriptionPrinter.append("This option can be used to separate command-line options from the " + "list of argument, (useful when arguments might be mistaken for command-line options").newline(); descriptionPrinter.newline(); // arguments name optionPrinter.append(UsageHelper.toDescription(arguments)).newline(); // description descriptionPrinter.append(arguments.getDescription()).newline(); descriptionPrinter.newline(); } } } private List sortOptions(List options) { if (optionComparator != null) { options = new ArrayList(options); Collections.sort(options, optionComparator); } return options; } } airline-0.7/src/main/java/io/airlift/airline/Context.java000066400000000000000000000001361242674174300234240ustar00rootroot00000000000000package io.airlift.airline; public enum Context { GLOBAL, GROUP, COMMAND, OPTION, ARGS } airline-0.7/src/main/java/io/airlift/airline/GlobalSuggester.java000066400000000000000000000015301242674174300250700ustar00rootroot00000000000000package io.airlift.airline; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.OptionMetadata; import javax.inject.Inject; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.transform; public class GlobalSuggester implements Suggester { @Inject public GlobalMetadata metadata; @Override public Iterable suggest() { return concat( transform(metadata.getCommandGroups(), CommandGroupMetadata.nameGetter()), transform(metadata.getDefaultGroupCommands(), CommandMetadata.nameGetter()), concat(transform(metadata.getOptions(), OptionMetadata.optionsGetter())) ); } } airline-0.7/src/main/java/io/airlift/airline/GlobalUsage.java000066400000000000000000000101271242674174300241660ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Preconditions; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.OptionMetadata; import javax.annotation.Nullable; import java.util.Collections; import java.util.Comparator; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.UsageHelper.DEFAULT_OPTION_COMPARATOR; public class GlobalUsage { private final int columnSize; private final Comparator optionComparator; public GlobalUsage() { this(79, DEFAULT_OPTION_COMPARATOR); } public GlobalUsage(int columnSize) { this(columnSize, DEFAULT_OPTION_COMPARATOR); } public GlobalUsage(int columnSize, @Nullable Comparator optionComparator) { Preconditions.checkArgument(columnSize > 0, "columnSize must be greater than 0"); this.columnSize = columnSize; this.optionComparator = optionComparator; } /** * Display the help on System.out. */ public void usage(GlobalMetadata global) { StringBuilder stringBuilder = new StringBuilder(); usage(global, stringBuilder); System.out.println(stringBuilder.toString()); } /** * Store the help in the passed string builder. */ public void usage(GlobalMetadata global, StringBuilder out) { usage(global, new UsagePrinter(out, columnSize)); } public void usage(GlobalMetadata global, UsagePrinter out) { // // NAME // out.append("NAME").newline(); out.newIndentedPrinter(8) .append(global.getName()) .append("-") .append(global.getDescription()) .newline() .newline(); // // SYNOPSIS // out.append("SYNOPSIS").newline(); out.newIndentedPrinter(8).newPrinterWithHangingIndent(8) .append(global.getName()) .appendWords(UsageHelper.toSynopsisUsage(global.getOptions())) .append(" []") .newline() .newline(); // // OPTIONS // List options = newArrayList(global.getOptions()); if (options.size() > 0) { if (optionComparator != null) { Collections.sort(options, optionComparator); } out.append("OPTIONS").newline(); for (OptionMetadata option : options) { // option names UsagePrinter optionPrinter = out.newIndentedPrinter(8); optionPrinter.append(UsageHelper.toDescription(option)).newline(); // description UsagePrinter descriptionPrinter = optionPrinter.newIndentedPrinter(4); descriptionPrinter.append(option.getDescription()).newline(); descriptionPrinter.newline(); } } // // COMMANDS // out.append("COMMANDS").newline(); UsagePrinter commandPrinter = out.newIndentedPrinter(8); for (CommandMetadata command : global.getDefaultGroupCommands()) { printCommandDescription(commandPrinter, null, command); } for (CommandGroupMetadata group : global.getCommandGroups()) { for (CommandMetadata command : group.getCommands()) { printCommandDescription(commandPrinter, group, command); } } } private void printCommandDescription(UsagePrinter commandPrinter, @Nullable CommandGroupMetadata group, CommandMetadata command) { if (group != null) { commandPrinter.append(group.getName()); } commandPrinter.append(command.getName()).newline(); if (command.getDescription() != null) { commandPrinter.newIndentedPrinter(4).append(command.getDescription()).newline(); } commandPrinter.newline(); } } airline-0.7/src/main/java/io/airlift/airline/GlobalUsageSummary.java000066400000000000000000000067011242674174300255470ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Function; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.OptionMetadata; import java.util.List; import java.util.Map; import java.util.Map.Entry; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newTreeMap; import static io.airlift.airline.UsageHelper.toUsage; public class GlobalUsageSummary { private final int columnSize; public GlobalUsageSummary() { this(79); } public GlobalUsageSummary(int columnSize) { Preconditions.checkArgument(columnSize > 0, "columnSize must be greater than 0"); this.columnSize = columnSize; } /** * Display the help on System.out. */ public void usage(GlobalMetadata global) { StringBuilder stringBuilder = new StringBuilder(); usage(global, stringBuilder); System.out.println(stringBuilder.toString()); } /** * Store the help in the passed string builder. */ public void usage(GlobalMetadata global, StringBuilder out) { usage(global, new UsagePrinter(out, columnSize)); } public void usage(GlobalMetadata global, UsagePrinter out) { // // Usage // // build arguments List commandArguments = newArrayList(); commandArguments.addAll(Collections2.transform(global.getOptions(), new Function() { public String apply(OptionMetadata option) { if (option.isHidden()) { return null; } return toUsage(option); } })); out.newPrinterWithHangingIndent(8) .append("usage:") .append(global.getName()) .appendWords(commandArguments) .append(" []") .newline() .newline(); // // Common commands // Map commands = newTreeMap(); for (CommandMetadata commandMetadata : global.getDefaultGroupCommands()) { if (!commandMetadata.isHidden()) { commands.put(commandMetadata.getName(), commandMetadata.getDescription()); } } for (CommandGroupMetadata commandGroupMetadata : global.getCommandGroups()) { commands.put(commandGroupMetadata.getName(), commandGroupMetadata.getDescription()); } out.append("The most commonly used ").append(global.getName()).append(" commands are:").newline(); out.newIndentedPrinter(4).appendTable(Iterables.transform(commands.entrySet(), new Function, Iterable>() { public Iterable apply(Entry entry) { return ImmutableList.of(entry.getKey(), Objects.firstNonNull(entry.getValue(), "")); } })); out.newline(); out.append("See").append("'" + global.getName()).append("help ' for more information on a specific command.").newline(); } } airline-0.7/src/main/java/io/airlift/airline/GroupSuggester.java000066400000000000000000000013011242674174300247600ustar00rootroot00000000000000package io.airlift.airline; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.OptionMetadata; import javax.inject.Inject; import static com.google.common.collect.Iterables.concat; import static com.google.common.collect.Iterables.transform; public class GroupSuggester implements Suggester { @Inject public CommandGroupMetadata group; @Override public Iterable suggest() { return concat( transform(group.getCommands(), CommandMetadata.nameGetter()), concat(transform(group.getOptions(), OptionMetadata.optionsGetter())) ); } } airline-0.7/src/main/java/io/airlift/airline/Help.java000066400000000000000000000060531242674174300226740ustar00rootroot00000000000000package io.airlift.airline; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import javax.inject.Inject; import java.util.List; import java.util.concurrent.Callable; import static com.google.common.collect.Lists.newArrayList; @Command(name = "help", description = "Display help information") public class Help implements Runnable, Callable { @Inject public GlobalMetadata global; @Arguments public List command = newArrayList(); @Override public void run() { help(global, command); } @Override public Void call() { run(); return null; } public static void help(CommandMetadata command) { StringBuilder stringBuilder = new StringBuilder(); help(command, stringBuilder); System.out.println(stringBuilder.toString()); } public static void help(CommandMetadata command, StringBuilder out) { new CommandUsage().usage(null, null, command.getName(), command, out); } public static void help(GlobalMetadata global, List commandNames) { StringBuilder stringBuilder = new StringBuilder(); help(global, commandNames, stringBuilder); System.out.println(stringBuilder.toString()); } public static void help(GlobalMetadata global, List commandNames, StringBuilder out) { if (commandNames.isEmpty()) { new GlobalUsageSummary().usage(global, out); return; } String name = commandNames.get(0); // main program? if (name.equals(global.getName())) { new GlobalUsage().usage(global, out); return; } // command in the default group? for (CommandMetadata command : global.getDefaultGroupCommands()) { if (name.equals(command.getName())) { new CommandUsage().usage(global.getName(), null, command.getName(), command, out); return; } } // command in a group? for (CommandGroupMetadata group : global.getCommandGroups()) { if (name.endsWith(group.getName())) { // general group help or specific command help? if (commandNames.size() == 1) { new CommandGroupUsage().usage(global, group, out); return; } else { String commandName = commandNames.get(1); for (CommandMetadata command : group.getCommands()) { if (commandName.equals(command.getName())) { new CommandUsage().usage(global.getName(), group.getName(), command.getName(), command, out); return; } } System.out.println("Unknown command " + name + " " + commandName); } } } System.out.println("Unknown command " + name); } } airline-0.7/src/main/java/io/airlift/airline/HelpOption.java000066400000000000000000000007021242674174300240600ustar00rootroot00000000000000package io.airlift.airline; import io.airlift.airline.model.CommandMetadata; import javax.inject.Inject; public class HelpOption { @Inject public CommandMetadata commandMetadata; @Option(name = {"-h", "--help"}, description = "Display help information") public Boolean help = false; public boolean showHelpIfRequested() { if (help) { Help.help(commandMetadata); } return help; } } airline-0.7/src/main/java/io/airlift/airline/Option.java000066400000000000000000000035501242674174300232530ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import java.lang.annotation.Retention; import java.lang.annotation.Target; import static java.lang.annotation.ElementType.FIELD; @Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @Target({FIELD}) public @interface Option { /** * Is this a command, group or global option */ OptionType type() default OptionType.COMMAND; /** * Name use to identify the option value in documentation and error messages. */ String title() default ""; /** * An array of allowed command line parameters (e.g. "-n", "--name", etc...). */ String[] name(); /** * A description of this option. */ String description() default ""; /** * Whether this option is required. */ boolean required() default false; /** * How many parameter values this option will consume. For example, * an arity of 2 will allow "-pair value1 value2". */ int arity() default Integer.MIN_VALUE; /** * If true, this parameter won't appear in the usage(). */ boolean hidden() default false; String[] allowedValues() default {}; } airline-0.7/src/main/java/io/airlift/airline/OptionType.java000066400000000000000000000001231242674174300241060ustar00rootroot00000000000000package io.airlift.airline; public enum OptionType { GLOBAL, GROUP, COMMAND } airline-0.7/src/main/java/io/airlift/airline/ParseArgumentsMissingException.java000066400000000000000000000021621242674174300301520ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseArgumentsMissingException extends ParseException { private final String argumentTitle; ParseArgumentsMissingException(String argumentTitle) { super("Required parameters are missing: %s", argumentTitle); this.argumentTitle = argumentTitle; } public String getArgumentTitle() { return argumentTitle; } } airline-0.7/src/main/java/io/airlift/airline/ParseArgumentsUnexpectedException.java000066400000000000000000000023451242674174300306500ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import java.util.List; public class ParseArgumentsUnexpectedException extends ParseException { private final List unparsedInput; ParseArgumentsUnexpectedException(List unparsedInput) { super("Found unexpected parameters: %s", unparsedInput); this.unparsedInput = ImmutableList.copyOf(unparsedInput); } public List getUnparsedInput() { return unparsedInput; } } airline-0.7/src/main/java/io/airlift/airline/ParseCommandMissingException.java000066400000000000000000000016271242674174300275700ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseCommandMissingException extends ParseException { ParseCommandMissingException() { super("No command specified"); } } airline-0.7/src/main/java/io/airlift/airline/ParseCommandUnrecognizedException.java000066400000000000000000000023501242674174300306050ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import java.util.List; public class ParseCommandUnrecognizedException extends ParseException { private final List unparsedInput; ParseCommandUnrecognizedException(List unparsedInput) { super("Command '%s' not recognized", unparsedInput.get(0)); this.unparsedInput = ImmutableList.copyOf(unparsedInput); } public List getUnparsedInput() { return unparsedInput; } } airline-0.7/src/main/java/io/airlift/airline/ParseException.java000066400000000000000000000020601242674174300247270ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseException extends RuntimeException { public ParseException(String string, Object... args) { super(String.format(string, args)); } public ParseException(Exception cause, String string, Object... args) { super(String.format(string, args), cause); } } airline-0.7/src/main/java/io/airlift/airline/ParseOptionConversionException.java000066400000000000000000000026321242674174300301730ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseOptionConversionException extends ParseException { private final String optionTitle; private final String value; private final String typeName; ParseOptionConversionException(String optionTitle, String value, String typeName) { super("%s: can not convert \"%s\" to a %s", optionTitle, value, typeName); this.optionTitle = optionTitle; this.value = value; this.typeName = typeName; } public String getOptionTitle() { return optionTitle; } public String getValue() { return value; } public String getTypeName() { return typeName; } } airline-0.7/src/main/java/io/airlift/airline/ParseOptionMissingException.java000066400000000000000000000021321242674174300274520ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseOptionMissingException extends ParseException { private final String optionTitle; ParseOptionMissingException(String optionTitle) { super("Required option '%s' is missing", optionTitle); this.optionTitle = optionTitle; } public String getOptionTitle() { return optionTitle; } } airline-0.7/src/main/java/io/airlift/airline/ParseOptionMissingValueException.java000066400000000000000000000021611242674174300304510ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; public class ParseOptionMissingValueException extends ParseException { private final String optionTitle; ParseOptionMissingValueException(String optionTitle) { super("Required values for option '%s' not provided", optionTitle); this.optionTitle = optionTitle; } public String getOptionTitle() { return optionTitle; } } airline-0.7/src/main/java/io/airlift/airline/ParseState.java000066400000000000000000000120141242674174300240510ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ListMultimap; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.OptionMetadata; import java.util.List; public class ParseState { private final List locationStack; private final CommandGroupMetadata group; private final CommandMetadata command; private final ListMultimap parsedOptions; private final List parsedArguments; private final OptionMetadata currentOption; private final List unparsedInput; ParseState(CommandGroupMetadata group, CommandMetadata command, ListMultimap parsedOptions, List locationStack, List parsedArguments, OptionMetadata currentOption, List unparsedInput) { this.group = group; this.command = command; this.parsedOptions = parsedOptions; this.locationStack = locationStack; this.parsedArguments = parsedArguments; this.currentOption = currentOption; this.unparsedInput = unparsedInput; } public static ParseState newInstance() { return new ParseState(null, null, ArrayListMultimap.create(), ImmutableList.of(), ImmutableList.of(), null, ImmutableList.of()); } public ParseState pushContext(Context location) { ImmutableList locationStack = ImmutableList.builder() .addAll(this.locationStack) .add(location) .build(); return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, currentOption, unparsedInput); } public ParseState popContext() { ImmutableList locationStack = ImmutableList.copyOf(this.locationStack.subList(0, this.locationStack.size() - 1)); return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, currentOption, unparsedInput); } public ParseState withOptionValue(OptionMetadata option, Object value) { ImmutableListMultimap newOptions = ImmutableListMultimap.builder() .putAll(parsedOptions) .put(option, value) .build(); return new ParseState(group, command, newOptions, locationStack, parsedArguments, currentOption, unparsedInput); } public ParseState withGroup(CommandGroupMetadata group) { return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, currentOption, unparsedInput); } public ParseState withCommand(CommandMetadata command) { return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, currentOption, unparsedInput); } public ParseState withOption(OptionMetadata option) { return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, option, unparsedInput); } public ParseState withArgument(Object argument) { ImmutableList newArguments = ImmutableList.builder() .addAll(parsedArguments) .add(argument) .build(); return new ParseState(group, command, parsedOptions, locationStack, newArguments, currentOption, unparsedInput); } public ParseState withUnparsedInput(String input) { ImmutableList newUnparsedInput = ImmutableList.builder() .addAll(unparsedInput) .add(input) .build(); return new ParseState(group, command, parsedOptions, locationStack, parsedArguments, currentOption, newUnparsedInput); } @Override public String toString() { return "ParseState{" + "locationStack=" + locationStack + ", group=" + group + ", command=" + command + ", parsedOptions=" + parsedOptions + ", parsedArguments=" + parsedArguments + ", currentOption=" + currentOption + ", unparsedInput=" + unparsedInput + '}'; } public Context getLocation() { return locationStack.get(locationStack.size() - 1); } public CommandGroupMetadata getGroup() { return group; } public CommandMetadata getCommand() { return command; } public OptionMetadata getCurrentOption() { return currentOption; } public ListMultimap getParsedOptions() { return parsedOptions; } public List getParsedArguments() { return parsedArguments; } public List getUnparsedInput() { return unparsedInput; } } airline-0.7/src/main/java/io/airlift/airline/Parser.java000066400000000000000000000242221242674174300232360ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.PeekingIterator; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.OptionMetadata; import java.util.List; import java.util.regex.Pattern; import static com.google.common.base.Predicates.compose; import static com.google.common.base.Predicates.equalTo; import static com.google.common.collect.Iterables.find; public class Parser { private static final Pattern SHORT_OPTIONS_PATTERN = Pattern.compile("-[^-].*"); // global> (option value*)* (group (option value*)*)? (command (option value* | arg)* '--'? args*)? public ParseState parse(GlobalMetadata metadata, String... params) { return parse(metadata, ImmutableList.copyOf(params)); } public ParseState parse(GlobalMetadata metadata, Iterable params) { PeekingIterator tokens = Iterators.peekingIterator(params.iterator()); ParseState state = ParseState.newInstance().pushContext(Context.GLOBAL); // parse global options state = parseOptions(tokens, state, metadata.getOptions()); // parse group if (tokens.hasNext()) { CommandGroupMetadata group = find(metadata.getCommandGroups(), compose(equalTo(tokens.peek()), CommandGroupMetadata.nameGetter()), null); if (group != null) { tokens.next(); state = state.withGroup(group).pushContext(Context.GROUP); state = parseOptions(tokens, state, state.getGroup().getOptions()); } } // parse command List expectedCommands = metadata.getDefaultGroupCommands(); if (state.getGroup() != null) { expectedCommands = state.getGroup().getCommands(); } if (tokens.hasNext()) { CommandMetadata command = find(expectedCommands, compose(equalTo(tokens.peek()), CommandMetadata.nameGetter()), null); if (command == null) { while (tokens.hasNext()) { state = state.withUnparsedInput(tokens.next()); } } else { tokens.next(); state = state.withCommand(command).pushContext(Context.COMMAND); while (tokens.hasNext()) { state = parseOptions(tokens, state, command.getCommandOptions()); state = parseArgs(state, tokens, command.getArguments()); } } } return state; } public ParseState parseCommand(CommandMetadata command, Iterable params) { PeekingIterator tokens = Iterators.peekingIterator(params.iterator()); ParseState state = ParseState.newInstance().pushContext(Context.GLOBAL).withCommand(command); while (tokens.hasNext()) { state = parseOptions(tokens, state, command.getCommandOptions()); state = parseArgs(state, tokens, command.getArguments()); } return state; } private ParseState parseOptions(PeekingIterator tokens, ParseState state, List allowedOptions) { while (tokens.hasNext()) { // // Try to parse next option(s) using different styles. If code matches it returns // the next parser state, otherwise it returns null. // Parse a simple option ParseState nextState = parseSimpleOption(tokens, state, allowedOptions); if (nextState != null) { state = nextState; continue; } // Parse GNU getopt long-form: --option=value nextState = parseLongGnuGetOpt(tokens, state, allowedOptions); if (nextState != null) { state = nextState; continue; } // Handle classic getopt syntax: -abc nextState = parseClassicGetOpt(tokens, state, allowedOptions); if (nextState != null) { state = nextState; continue; } // did not match an option break; } return state; } private ParseState parseSimpleOption(PeekingIterator tokens, ParseState state, List allowedOptions) { OptionMetadata option = findOption(allowedOptions, tokens.peek()); if (option == null) { return null; } tokens.next(); state = state.pushContext(Context.OPTION).withOption(option); Object value; if (option.getArity() == 0) { state = state.withOptionValue(option, Boolean.TRUE).popContext(); } else if (option.getArity() == 1) { if (tokens.hasNext()) { value = TypeConverter.newInstance().convert(option.getTitle(), option.getJavaType(), tokens.next()); state = state.withOptionValue(option, value).popContext(); } } else { ImmutableList.Builder values = ImmutableList.builder(); int count = 0; while (count < option.getArity() && tokens.hasNext()) { values.add(TypeConverter.newInstance().convert(option.getTitle(), option.getJavaType(), tokens.next())); ++count; } if (count == option.getArity()) { state = state.withOptionValue(option, values.build()).popContext(); } } return state; } private ParseState parseLongGnuGetOpt(PeekingIterator tokens, ParseState state, List allowedOptions) { List parts = ImmutableList.copyOf(Splitter.on('=').limit(2).split(tokens.peek())); if (parts.size() != 2) { return null; } OptionMetadata option = findOption(allowedOptions, parts.get(0)); if (option == null || option.getArity() != 1) { // TODO: this is not exactly correct. It should be an error condition return null; } // we have a match so consume the token tokens.next(); // update state state = state.pushContext(Context.OPTION).withOption(option); Object value = TypeConverter.newInstance().convert(option.getTitle(), option.getJavaType(), parts.get(1)); state = state.withOption(option).withOptionValue(option, value).popContext(); return state; } private ParseState parseClassicGetOpt(PeekingIterator tokens, ParseState state, List allowedOptions) { if (!SHORT_OPTIONS_PATTERN.matcher(tokens.peek()).matches()) { return null; } // remove leading dash from token String remainingToken = tokens.peek().substring(1); ParseState nextState = state; while (!remainingToken.isEmpty()) { char tokenCharacter = remainingToken.charAt(0); // is the current token character a single letter option? OptionMetadata option = findOption(allowedOptions, "-" + tokenCharacter); if (option == null) { return null; } nextState = nextState.pushContext(Context.OPTION).withOption(option); // remove current token character remainingToken = remainingToken.substring(1); // for no argument options, process the option and remove the character from the token if (option.getArity() == 0) { nextState = nextState.withOptionValue(option, Boolean.TRUE).popContext(); continue; } if (option.getArity() == 1) { // we must, consume the current token so we can see the next token tokens.next(); // if current token has more characters, this is the value; otherwise it is the next token if (!remainingToken.isEmpty()) { Object value = TypeConverter.newInstance().convert(option.getTitle(), option.getJavaType(), remainingToken); nextState = nextState.withOptionValue(option, value).popContext(); } else if (tokens.hasNext()) { Object value = TypeConverter.newInstance().convert(option.getTitle(), option.getJavaType(), tokens.next()); nextState = nextState.withOptionValue(option, value).popContext(); } return nextState; } throw new UnsupportedOperationException("Short options style can not be used with option " + option.getAllowedValues()); } // consume the current token tokens.next(); return nextState; } private ParseState parseArgs(ParseState state, PeekingIterator tokens, ArgumentsMetadata arguments) { if (tokens.hasNext()) { if (tokens.peek().equals("--")) { state = state.pushContext(Context.ARGS); tokens.next(); // consume all args while (tokens.hasNext()) { state = parseArg(state, tokens, arguments); } } else { state = parseArg(state, tokens, arguments); } } return state; } private ParseState parseArg(ParseState state, PeekingIterator tokens, ArgumentsMetadata arguments) { if (arguments != null) { state = state.withArgument(TypeConverter.newInstance().convert(arguments.getTitle(), arguments.getJavaType(), tokens.next())); } else { state = state.withUnparsedInput(tokens.next()); } return state; } private OptionMetadata findOption(List options, String param) { for (OptionMetadata optionMetadata : options) { if (optionMetadata.getOptions().contains(param)) { return optionMetadata; } } return null; } } airline-0.7/src/main/java/io/airlift/airline/ParserUtil.java000066400000000000000000000043661242674174300241030ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.collect.ImmutableList; import com.google.common.collect.ListMultimap; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.OptionMetadata; import java.util.List; import java.util.Map; import static com.google.common.collect.Iterables.concat; public class ParserUtil { public static T createInstance(Class type) { if (type != null) { try { return type.getConstructor().newInstance(); } catch (Exception e) { throw new ParseException(e, "Unable to create instance %s", type.getName()); } } return null; } public static T createInstance(Class type, Iterable options, ListMultimap parsedOptions, ArgumentsMetadata arguments, Iterable parsedArguments, Iterable metadataInjection, Map, Object> bindings) { // create the command instance T commandInstance = (T) ParserUtil.createInstance(type); // inject options for (OptionMetadata option : options) { List values = parsedOptions.get(option); if (option.getArity() > 1 && !values.isEmpty()) { // hack: flatten the collection values = ImmutableList.copyOf(concat((Iterable>) values)); } if (values != null && !values.isEmpty()) { for (Accessor accessor : option.getAccessors()) { accessor.addValues(commandInstance, values); } } } // inject args if (arguments != null && parsedArguments != null) { for (Accessor accessor : arguments.getAccessors()) { accessor.addValues(commandInstance, parsedArguments); } } for (Accessor accessor : metadataInjection) { Object injectee = bindings.get(accessor.getJavaType()); if (injectee != null) { accessor.addValues(commandInstance, ImmutableList.of(injectee)); } } return commandInstance; } } airline-0.7/src/main/java/io/airlift/airline/SingleCommand.java000066400000000000000000000072061242674174300245250ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.MetadataLoader; import io.airlift.airline.model.OptionMetadata; import java.util.List; import static com.google.common.base.Preconditions.checkNotNull; import static io.airlift.airline.ParserUtil.createInstance; public class SingleCommand { public static SingleCommand singleCommand(Class command) { return new SingleCommand(command); } private final CommandMetadata commandMetadata; private SingleCommand(Class command) { checkNotNull(command, "command is null"); commandMetadata = MetadataLoader.loadCommand(command); } public CommandMetadata getCommandMetadata() { return commandMetadata; } public C parse(String... args) { return parse(ImmutableList.copyOf(args)); } public C parse(Iterable args) { checkNotNull(args, "args is null"); Parser parser = new Parser(); ParseState state = parser.parseCommand(commandMetadata, args); validate(state); CommandMetadata command = state.getCommand(); return createInstance(command.getType(), command.getAllOptions(), state.getParsedOptions(), command.getArguments(), state.getParsedArguments(), command.getMetadataInjections(), ImmutableMap., Object>of(CommandMetadata.class, commandMetadata)); } private void validate(ParseState state) { CommandMetadata command = state.getCommand(); if (command == null) { List unparsedInput = state.getUnparsedInput(); if (unparsedInput.isEmpty()) { throw new ParseCommandMissingException(); } else { throw new ParseCommandUnrecognizedException(unparsedInput); } } ArgumentsMetadata arguments = command.getArguments(); if (state.getParsedArguments().isEmpty() && arguments != null && arguments.isRequired()) { throw new ParseArgumentsMissingException(arguments.getTitle()); } if (!state.getUnparsedInput().isEmpty()) { throw new ParseArgumentsUnexpectedException(state.getUnparsedInput()); } if (state.getLocation() == Context.OPTION) { throw new ParseOptionMissingValueException(state.getCurrentOption().getTitle()); } for (OptionMetadata option : command.getAllOptions()) { if (option.isRequired() && !state.getParsedOptions().containsKey(option)) { throw new ParseOptionMissingException(option.getOptions().iterator().next()); } } } } airline-0.7/src/main/java/io/airlift/airline/SuggestCommand.java000066400000000000000000000055231242674174300247250ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import io.airlift.airline.model.CommandGroupMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.GlobalMetadata; import io.airlift.airline.model.MetadataLoader; import io.airlift.airline.model.OptionMetadata; import io.airlift.airline.model.SuggesterMetadata; import javax.inject.Inject; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.ParserUtil.createInstance; @Command(name = "suggest") public class SuggestCommand implements Runnable, Callable { private static final Map> BUILTIN_SUGGESTERS = ImmutableMap.>builder() .put(Context.GLOBAL, GlobalSuggester.class) .put(Context.GROUP, GroupSuggester.class) .put(Context.COMMAND, CommandSuggester.class) .build(); @Inject public GlobalMetadata metadata; @Arguments public List arguments = newArrayList(); @VisibleForTesting public Iterable generateSuggestions() { Parser parser = new Parser(); ParseState state = parser.parse(metadata, arguments); Class suggesterClass = BUILTIN_SUGGESTERS.get(state.getLocation()); if (suggesterClass != null) { SuggesterMetadata suggesterMetadata = MetadataLoader.loadSuggester(suggesterClass); if (suggesterMetadata != null) { ImmutableMap.Builder, Object> bindings = ImmutableMap., Object>builder() .put(GlobalMetadata.class, metadata); if (state.getGroup() != null) { bindings.put(CommandGroupMetadata.class, state.getGroup()); } if (state.getCommand() != null) { bindings.put(CommandMetadata.class, state.getCommand()); } Suggester suggester = createInstance(suggesterMetadata.getSuggesterClass(), ImmutableList.of(), null, null, null, suggesterMetadata.getMetadataInjections(), bindings.build()); return suggester.suggest(); } } return ImmutableList.of(); } @Override public void run() { System.out.println(Joiner.on("\n").join(generateSuggestions())); } @Override public Void call() { run(); return null; } } airline-0.7/src/main/java/io/airlift/airline/Suggester.java000066400000000000000000000001341242674174300237460ustar00rootroot00000000000000package io.airlift.airline; public interface Suggester { Iterable suggest(); } airline-0.7/src/main/java/io/airlift/airline/TypeConverter.java000066400000000000000000000054071242674174300246170ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Preconditions; import java.lang.reflect.Constructor; import java.lang.reflect.Method; public class TypeConverter { public static TypeConverter newInstance() { return new TypeConverter(); } public Object convert(String name, Class type, String value) { Preconditions.checkNotNull(name, "name is null"); Preconditions.checkNotNull(type, "type is null"); Preconditions.checkNotNull(value, "value is null"); try { if (String.class.isAssignableFrom(type)) { return value; } else if (Boolean.class.isAssignableFrom(type) || Boolean.TYPE.isAssignableFrom(type)) { return Boolean.valueOf(value); } else if (Byte.class.isAssignableFrom(type) || Byte.TYPE.isAssignableFrom(type)) { return Byte.valueOf(value); } else if (Short.class.isAssignableFrom(type) || Short.TYPE.isAssignableFrom(type)) { return Short.valueOf(value); } else if (Integer.class.isAssignableFrom(type) || Integer.TYPE.isAssignableFrom(type)) { return Integer.valueOf(value); } else if (Long.class.isAssignableFrom(type) || Long.TYPE.isAssignableFrom(type)) { return Long.valueOf(value); } else if (Float.class.isAssignableFrom(type) || Float.TYPE.isAssignableFrom(type)) { return Float.valueOf(value); } else if (Double.class.isAssignableFrom(type) || Double.TYPE.isAssignableFrom(type)) { return Double.valueOf(value); } } catch (Exception ignored) { } // Look for a static fromString(String) method try { Method valueOf = type.getMethod("fromString", String.class); if (valueOf.getReturnType().isAssignableFrom(type)) { return valueOf.invoke(null, value); } } catch (Throwable ignored) { } // Look for a static valueOf(String) method (this covers enums which have a valueOf method) try { Method valueOf = type.getMethod("valueOf", String.class); if (valueOf.getReturnType().isAssignableFrom(type)) { return valueOf.invoke(null, value); } } catch (Throwable ignored) { } // Look for a constructor taking a string try { Constructor constructor = type.getConstructor(String.class); return constructor.newInstance(value); } catch (Throwable ignored) { } throw new ParseOptionConversionException(name, value, type.getSimpleName()); } } airline-0.7/src/main/java/io/airlift/airline/UsageHelper.java000066400000000000000000000134211242674174300242050ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.ComparisonChain; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import io.airlift.airline.model.ArgumentsMetadata; import io.airlift.airline.model.CommandMetadata; import io.airlift.airline.model.OptionMetadata; import javax.annotation.Nullable; import java.util.Comparator; import java.util.List; import java.util.Set; import static com.google.common.collect.Iterables.filter; import static com.google.common.collect.Iterables.transform; import static io.airlift.airline.model.OptionMetadata.isHiddenPredicate; public class UsageHelper { public static final Comparator DEFAULT_OPTION_COMPARATOR = new Comparator() { @Override public int compare(OptionMetadata o1, OptionMetadata o2) { String option1 = o1.getOptions().iterator().next(); option1 = option1.replaceFirst("^-+", ""); String option2 = o2.getOptions().iterator().next(); option2 = option2.replaceFirst("^-+", ""); return ComparisonChain.start() .compare(option1.toLowerCase(), option2.toLowerCase()) .compare(option2, option1) // print lower case letters before upper case .compare(System.identityHashCode(o1), System.identityHashCode(o2)) .result(); } }; public static final Comparator DEFAULT_COMMAND_COMPARATOR = new Comparator() { @Override public int compare(CommandMetadata o1, CommandMetadata o2) { return ComparisonChain.start() .compare(o1.getName().toLowerCase(), o2.getName().toLowerCase()) .compare(o2.getName(), o1.getName()) // print lower case letters before upper case .compare(System.identityHashCode(o1), System.identityHashCode(o2)) .result(); } }; public static String toDescription(OptionMetadata option) { Set options = option.getOptions(); StringBuilder stringBuilder = new StringBuilder(); final String argumentString; if (option.getArity() > 0) { argumentString = Joiner.on(" ").join(Lists.transform(ImmutableList.of(option.getTitle()), new Function() { public String apply(@Nullable String argument) { return "<" + argument + ">"; } })); } else { argumentString = null; } Joiner.on(", ").appendTo(stringBuilder, transform(options, new Function() { public String apply(@Nullable String option) { if (argumentString != null) { return option + " " + argumentString; } return option; } })); return stringBuilder.toString(); } public static String toDescription(ArgumentsMetadata arguments) { if (!arguments.getUsage().isEmpty()) { return arguments.getUsage(); } return "<" + arguments.getTitle() + ">"; } public static String toUsage(OptionMetadata option) { Set options = option.getOptions(); boolean required = option.isRequired(); StringBuilder stringBuilder = new StringBuilder(); if (!required) { stringBuilder.append('['); } if (options.size() > 1) { stringBuilder.append('('); } final String argumentString; if (option.getArity() > 0) { argumentString = Joiner.on(" ").join(transform(ImmutableList.of(option.getTitle()), new Function() { public String apply(@Nullable String argument) { return "<" + argument + ">"; } })); } else { argumentString = null; } Joiner.on(" | ").appendTo(stringBuilder, transform(options, new Function() { public String apply(@Nullable String option) { if (argumentString != null) { return option + " " + argumentString; } else { return option; } } })); if (options.size() > 1) { stringBuilder.append(')'); } if (option.isMultiValued()) { stringBuilder.append("..."); } if (!required) { stringBuilder.append(']'); } return stringBuilder.toString(); } public static String toUsage(ArgumentsMetadata arguments) { if (!arguments.getUsage().isEmpty()) { return arguments.getUsage(); } boolean required = arguments.isRequired(); StringBuilder stringBuilder = new StringBuilder(); if (!required) { stringBuilder.append('['); } stringBuilder.append("<").append(arguments.getTitle()).append(">"); if (arguments.isMultiValued()) { stringBuilder.append("..."); } if (!required) { stringBuilder.append(']'); } return stringBuilder.toString(); } public static List toSynopsisUsage(List options) { return ImmutableList.copyOf(transform(filter(options, isHiddenPredicate()), new Function() { public String apply(OptionMetadata option) { return toUsage(option); } })); } } airline-0.7/src/main/java/io/airlift/airline/UsagePrinter.java000066400000000000000000000074111242674174300244130ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Splitter; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static com.google.common.collect.Lists.newArrayList; public class UsagePrinter { private final StringBuilder out; private final int maxSize; private final int indent; private final int hangingIndent; private final AtomicInteger currentPosition; public UsagePrinter(StringBuilder out) { this(out, 79); } public UsagePrinter(StringBuilder out, int maxSize) { this(out, maxSize, 0, 0, new AtomicInteger()); } public UsagePrinter(StringBuilder out, int maxSize, int indent, int hangingIndent, AtomicInteger currentPosition) { this.out = out; this.maxSize = maxSize; this.indent = indent; this.hangingIndent = hangingIndent; this.currentPosition = currentPosition; } public UsagePrinter newIndentedPrinter(int size) { return new UsagePrinter(out, maxSize, indent + size, hangingIndent, currentPosition); } public UsagePrinter newPrinterWithHangingIndent(int size) { return new UsagePrinter(out, maxSize, indent, hangingIndent + size, currentPosition); } public UsagePrinter newline() { out.append("\n"); currentPosition.set(0); return this; } public UsagePrinter appendTable(Iterable> table) { List columnSizes = newArrayList(); for (Iterable row : table) { int column = 0; for (String value : row) { while (column >= columnSizes.size()) { columnSizes.add(0); } columnSizes.set(column, Math.max(value.length(), columnSizes.get(column))); column++; } } if (currentPosition.get() != 0) { currentPosition.set(0); out.append("\n"); } for (Iterable row : table) { int column = 0; StringBuilder line = new StringBuilder(); for (String value : row) { int columnSize = columnSizes.get(column); line.append(value); line.append(spaces(columnSize - value.length())); line.append(" "); column++; } out.append(spaces(indent)).append(line.toString().trim()).append("\n"); } return this; } public UsagePrinter append(String value) { if (value == null) { return this; } return appendWords(Splitter.onPattern("\\s+").omitEmptyStrings().trimResults().split(String.valueOf(value))); } public UsagePrinter appendWords(Iterable words) { for (String word : words) { if (currentPosition.get() == 0) { // beginning of line out.append(spaces(indent)); currentPosition.getAndAdd((indent)); } else if (word.length() > maxSize || currentPosition.get() + word.length() <= maxSize) { // between words out.append(" "); currentPosition.getAndIncrement(); } else { // wrap line out.append("\n").append(spaces(indent)).append(spaces(hangingIndent)); currentPosition.set(indent); } out.append(word); currentPosition.getAndAdd((word.length())); } return this; } private static String spaces(int count) { StringBuilder result = new StringBuilder(); for (int i = 0; i < count; i++) { result.append(" "); } return result.toString(); } } airline-0.7/src/main/java/io/airlift/airline/model/000077500000000000000000000000001242674174300222355ustar00rootroot00000000000000airline-0.7/src/main/java/io/airlift/airline/model/ArgumentsMetadata.java000066400000000000000000000075701242674174300265170ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import io.airlift.airline.Accessor; import java.lang.reflect.Field; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; public class ArgumentsMetadata { private final String title; private final String description; private final String usage; private final boolean required; private final Set accessors; public ArgumentsMetadata(String title, String description, String usage, boolean required, Iterable path) { Preconditions.checkNotNull(title, "title is null"); Preconditions.checkNotNull(path, "path is null"); Preconditions.checkArgument(!Iterables.isEmpty(path), "path is empty"); this.title = title; this.description = description; this.usage = usage; this.required = required; this.accessors = ImmutableSet.of(new Accessor(path)); } public ArgumentsMetadata(Iterable arguments) { Preconditions.checkNotNull(arguments, "arguments is null"); Preconditions.checkArgument(!Iterables.isEmpty(arguments), "arguments is empty"); ArgumentsMetadata first = arguments.iterator().next(); this.title = first.title; this.description = first.description; this.usage = first.usage; this.required = first.required; Set accessors = newHashSet(); for (ArgumentsMetadata other : arguments) { Preconditions.checkArgument(first.equals(other), "Conflicting arguments definitions: %s, %s", first, other); accessors.addAll(other.getAccessors()); } this.accessors = ImmutableSet.copyOf(accessors); } public String getTitle() { return title; } public String getDescription() { return description; } public String getUsage() { return usage; } public boolean isRequired() { return required; } public Set getAccessors() { return accessors; } public boolean isMultiValued() { return accessors.iterator().next().isMultiValued(); } public Class getJavaType() { return accessors.iterator().next().getJavaType(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ArgumentsMetadata that = (ArgumentsMetadata) o; if (required != that.required) { return false; } if (description != null ? !description.equals(that.description) : that.description != null) { return false; } if (!title.equals(that.title)) { return false; } if (usage != null ? !usage.equals(that.usage) : that.usage != null) { return false; } return true; } @Override public int hashCode() { int result = title.hashCode(); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + (usage != null ? usage.hashCode() : 0); result = 31 * result + (required ? 1 : 0); return result; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("ArgumentsMetadata"); sb.append("{title='").append(title).append('\''); sb.append(", description='").append(description).append('\''); sb.append(", usage='").append(usage).append('\''); sb.append(", required=").append(required); sb.append(", accessors=").append(accessors); sb.append('}'); return sb.toString(); } } airline-0.7/src/main/java/io/airlift/airline/model/CommandGroupMetadata.java000066400000000000000000000037061242674174300271420ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import java.util.List; public class CommandGroupMetadata { private final String name; private final String description; private final List options; private final CommandMetadata defaultCommand; private final List commands; public CommandGroupMetadata(String name, String description, Iterable options, CommandMetadata defaultCommand, Iterable commands) { this.name = name; this.description = description; this.options = ImmutableList.copyOf(options); this.defaultCommand = defaultCommand; this.commands = ImmutableList.copyOf(commands); } public String getName() { return name; } public String getDescription() { return description; } public List getOptions() { return options; } public CommandMetadata getDefaultCommand() { return defaultCommand; } public List getCommands() { return commands; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("CommandGroupMetadata"); sb.append("{name='").append(name).append('\''); sb.append(", description='").append(description).append('\''); sb.append(", options=").append(options); sb.append(", defaultCommand=").append(defaultCommand); sb.append(", commands=").append(commands); sb.append('}'); return sb.toString(); } public static Function nameGetter() { return new Function() { public String apply(CommandGroupMetadata input) { return input.getName(); } }; } } airline-0.7/src/main/java/io/airlift/airline/model/CommandMetadata.java000066400000000000000000000063461242674174300261300ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import io.airlift.airline.Accessor; import java.util.List; public class CommandMetadata { private final String name; private final String description; private final boolean hidden; private final List globalOptions; private final List groupOptions; private final List commandOptions; private final ArgumentsMetadata arguments; private final List metadataInjections; private final Class type; public CommandMetadata(String name, String description, boolean hidden, Iterable globalOptions, Iterable groupOptions, Iterable commandOptions, ArgumentsMetadata arguments, Iterable metadataInjections, Class type) { this.name = name; this.description = description; this.hidden = hidden; this.globalOptions = ImmutableList.copyOf(globalOptions); this.groupOptions = ImmutableList.copyOf(groupOptions); this.commandOptions = ImmutableList.copyOf(commandOptions); this.arguments = arguments; this.metadataInjections = ImmutableList.copyOf(metadataInjections); this.type = type; } public String getName() { return name; } public String getDescription() { return description; } public boolean isHidden() { return hidden; } public List getAllOptions() { return ImmutableList.builder().addAll(globalOptions).addAll(groupOptions).addAll(commandOptions).build(); } public List getGlobalOptions() { return globalOptions; } public List getGroupOptions() { return groupOptions; } public List getCommandOptions() { return commandOptions; } public ArgumentsMetadata getArguments() { return arguments; } public List getMetadataInjections() { return metadataInjections; } public Class getType() { return type; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("CommandMetadata"); sb.append("{name='").append(name).append('\''); sb.append(", description='").append(description).append('\''); sb.append(", globalOptions=").append(globalOptions); sb.append(", groupOptions=").append(groupOptions); sb.append(", commandOptions=").append(commandOptions); sb.append(", arguments=").append(arguments); sb.append(", metadataInjections=").append(metadataInjections); sb.append(", type=").append(type); sb.append('}'); return sb.toString(); } public static Function nameGetter() { return new Function() { public String apply(CommandMetadata input) { return input.getName(); } }; } } airline-0.7/src/main/java/io/airlift/airline/model/GlobalMetadata.java000066400000000000000000000041211242674174300257370ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.collect.ImmutableList; import java.util.List; public class GlobalMetadata { private final String name; private final String description; private final List options; private final CommandMetadata defaultCommand; private final List defaultGroupCommands; private final List commandGroups; public GlobalMetadata(String name, String description, Iterable options, CommandMetadata defaultCommand, Iterable defaultGroupCommands, Iterable commandGroups) { this.name = name; this.description = description; this.options = ImmutableList.copyOf(options); this.defaultCommand = defaultCommand; this.defaultGroupCommands = ImmutableList.copyOf(defaultGroupCommands); this.commandGroups = ImmutableList.copyOf(commandGroups); } public String getName() { return name; } public String getDescription() { return description; } public List getOptions() { return options; } public CommandMetadata getDefaultCommand() { return defaultCommand; } public List getDefaultGroupCommands() { return defaultGroupCommands; } public List getCommandGroups() { return commandGroups; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("GlobalMetadata"); sb.append("{name='").append(name).append('\''); sb.append(", description='").append(description).append('\''); sb.append(", options=").append(options); sb.append(", defaultCommand=").append(defaultCommand); sb.append(", defaultGroupCommands=").append(defaultGroupCommands); sb.append(", commandGroups=").append(commandGroups); sb.append('}'); return sb.toString(); } } airline-0.7/src/main/java/io/airlift/airline/model/MetadataLoader.java000066400000000000000000000257551242674174300257650ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.common.collect.ListMultimap; import io.airlift.airline.Accessor; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import io.airlift.airline.OptionType; import io.airlift.airline.Suggester; import javax.annotation.Nullable; import javax.inject.Inject; import java.lang.reflect.Field; import java.util.Collection; import java.util.List; import java.util.Map; import static com.google.common.collect.Iterables.transform; import static com.google.common.collect.Lists.newArrayList; import static com.google.common.collect.Maps.newHashMap; public class MetadataLoader { public static GlobalMetadata loadGlobal(String name, String description, CommandMetadata defaultCommand, Iterable defaultGroupCommands, Iterable groups) { ImmutableList.Builder globalOptionsBuilder = ImmutableList.builder(); if (defaultCommand != null) { globalOptionsBuilder.addAll(defaultCommand.getGlobalOptions()); } for (CommandMetadata command : defaultGroupCommands) { globalOptionsBuilder.addAll(command.getGlobalOptions()); } for (CommandGroupMetadata group : groups) { for (CommandMetadata command : group.getCommands()) { globalOptionsBuilder.addAll(command.getGlobalOptions()); } } List globalOptions = mergeOptionSet(globalOptionsBuilder.build()); return new GlobalMetadata(name, description, globalOptions, defaultCommand, defaultGroupCommands, groups); } public static CommandGroupMetadata loadCommandGroup(String name, String description, CommandMetadata defaultCommand, Iterable commands) { ImmutableList.Builder groupOptionsBuilder = ImmutableList.builder(); if (defaultCommand != null) { groupOptionsBuilder.addAll(defaultCommand.getGroupOptions()); } for (CommandMetadata command : commands) { groupOptionsBuilder.addAll(command.getGroupOptions()); } List groupOptions = mergeOptionSet(groupOptionsBuilder.build()); return new CommandGroupMetadata(name, description, groupOptions, defaultCommand, commands); } public static ImmutableList loadCommands(Iterable> defaultCommands) { return ImmutableList.copyOf(Iterables.transform(defaultCommands, new Function, CommandMetadata>() { public CommandMetadata apply(Class commandType) { return loadCommand(commandType); } })); } public static CommandMetadata loadCommand(Class commandType) { Command command = null; for (Class cls = commandType; command == null && !Object.class.equals(cls); cls = cls.getSuperclass()) { command = cls.getAnnotation(Command.class); } Preconditions.checkArgument(command != null, "Command %s is not annotated with @Command", commandType.getName()); String name = command.name(); String description = command.description().isEmpty() ? null : command.description(); boolean hidden = command.hidden(); InjectionMetadata injectionMetadata = loadInjectionMetadata(commandType); CommandMetadata commandMetadata = new CommandMetadata( name, description, hidden, injectionMetadata.globalOptions, injectionMetadata.groupOptions, injectionMetadata.commandOptions, Iterables.getFirst(injectionMetadata.arguments, null), injectionMetadata.metadataInjections, commandType); return commandMetadata; } public static SuggesterMetadata loadSuggester(Class suggesterClass) { InjectionMetadata injectionMetadata = loadInjectionMetadata(suggesterClass); return new SuggesterMetadata(suggesterClass, injectionMetadata.metadataInjections); } public static InjectionMetadata loadInjectionMetadata(Class type) { InjectionMetadata injectionMetadata = new InjectionMetadata(); loadInjectionMetadata(type, injectionMetadata, ImmutableList.of()); injectionMetadata.compact(); return injectionMetadata; } public static void loadInjectionMetadata(Class type, InjectionMetadata injectionMetadata, List fields) { for (Class cls = type; !Object.class.equals(cls); cls = cls.getSuperclass()) { for (Field field : cls.getDeclaredFields()) { field.setAccessible(true); ImmutableList path = concat(fields, field); Inject injectAnnotation = field.getAnnotation(Inject.class); if (injectAnnotation != null) { if (field.getType().equals(GlobalMetadata.class) || field.getType().equals(CommandGroupMetadata.class) || field.getType().equals(CommandMetadata.class)) { injectionMetadata.metadataInjections.add(new Accessor(path)); } else { loadInjectionMetadata(field.getType(), injectionMetadata, path); } } Option optionAnnotation = field.getAnnotation(Option.class); if (optionAnnotation != null) { OptionType optionType = optionAnnotation.type(); String name; if (!optionAnnotation.title().isEmpty()) { name = optionAnnotation.title(); } else { name = field.getName(); } List options = ImmutableList.copyOf(optionAnnotation.name()); String description = optionAnnotation.description(); int arity = optionAnnotation.arity(); Preconditions.checkArgument(arity >= 0 || arity == Integer.MIN_VALUE, "Invalid arity for option %s", name); if (optionAnnotation.arity() >= 0) { arity = optionAnnotation.arity(); } else { Class fieldType = field.getType(); if (Boolean.class.isAssignableFrom(fieldType) || boolean.class.isAssignableFrom(fieldType)) { arity = 0; } else { arity = 1; } } boolean required = optionAnnotation.required(); boolean hidden = optionAnnotation.hidden(); List allowedValues = ImmutableList.copyOf(optionAnnotation.allowedValues()); if (allowedValues.isEmpty()) { allowedValues = null; } OptionMetadata optionMetadata = new OptionMetadata(optionType, options, name, description, arity, required, hidden, allowedValues, path); switch (optionType) { case GLOBAL: injectionMetadata.globalOptions.add(optionMetadata); break; case GROUP: injectionMetadata.groupOptions.add(optionMetadata); break; case COMMAND: injectionMetadata.commandOptions.add(optionMetadata); break; } } Arguments argumentsAnnotation = field.getAnnotation(Arguments.class); if (field.isAnnotationPresent(Arguments.class)) { String title; if (!argumentsAnnotation.title().isEmpty()) { title = argumentsAnnotation.title(); } else { title = field.getName(); } String description = argumentsAnnotation.description(); String usage = argumentsAnnotation.usage(); boolean required = argumentsAnnotation.required(); injectionMetadata.arguments.add(new ArgumentsMetadata(title, description, usage, required, path)); } } } } private static List mergeOptionSet(List options) { ListMultimap metadataIndex = ArrayListMultimap.create(); for (OptionMetadata option : options) { metadataIndex.put(option, option); } options = ImmutableList.copyOf(transform(metadataIndex.asMap().values(), new Function, OptionMetadata>() { @Override public OptionMetadata apply(@Nullable Collection options) { return new OptionMetadata(options); } })); Map optionIndex = newHashMap(); for (OptionMetadata option : options) { for (String optionName : option.getOptions()) { if (optionIndex.containsKey(optionName)) { throw new IllegalArgumentException(String.format("Fields %s and %s have conflicting definitions of option %s", optionIndex.get(optionName).getAccessors().iterator().next(), option.getAccessors().iterator().next(), optionName)); } optionIndex.put(optionName, option); } } return options; } private static ImmutableList concat(Iterable iterable, T item) { return ImmutableList.builder().addAll(iterable).add(item).build(); } private static class InjectionMetadata { private List globalOptions = newArrayList(); private List groupOptions = newArrayList(); private List commandOptions = newArrayList(); private List arguments = newArrayList(); private List metadataInjections = newArrayList(); private void compact() { globalOptions = mergeOptionSet(globalOptions); groupOptions = mergeOptionSet(groupOptions); commandOptions = mergeOptionSet(commandOptions); if (arguments.size() > 1) { arguments = ImmutableList.of(new ArgumentsMetadata(arguments)); } } } } airline-0.7/src/main/java/io/airlift/airline/model/OptionMetadata.java000066400000000000000000000152601242674174300260150ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import io.airlift.airline.Accessor; import io.airlift.airline.OptionType; import javax.annotation.Nullable; import java.lang.reflect.Field; import java.util.Set; import static com.google.common.collect.Sets.newHashSet; public class OptionMetadata { private final OptionType optionType; private final Set options; private final String title; private final String description; private final int arity; private final boolean required; private final boolean hidden; private final Set allowedValues; private final Set accessors; public OptionMetadata(OptionType optionType, Iterable options, String title, String description, int arity, boolean required, boolean hidden, Iterable allowedValues, Iterable path) { Preconditions.checkNotNull(optionType, "optionType is null"); Preconditions.checkNotNull(options, "options is null"); Preconditions.checkArgument(!Iterables.isEmpty(options), "options is empty"); Preconditions.checkNotNull(title, "title is null"); Preconditions.checkNotNull(path, "path is null"); Preconditions.checkArgument(!Iterables.isEmpty(path), "path is empty"); this.optionType = optionType; this.options = ImmutableSet.copyOf(options); this.title = title; this.description = description; this.arity = arity; this.required = required; this.hidden = hidden; if (allowedValues != null) { this.allowedValues = ImmutableSet.copyOf(allowedValues); } else { this.allowedValues = null; } this.accessors = ImmutableSet.of(new Accessor(path)); } public OptionMetadata(Iterable options) { Preconditions.checkNotNull(options, "options is null"); Preconditions.checkArgument(!Iterables.isEmpty(options), "options is empty"); OptionMetadata option = options.iterator().next(); this.optionType = option.optionType; this.options = option.options; this.title = option.title; this.description = option.description; this.arity = option.arity; this.required = option.required; this.hidden = option.hidden; if (option.allowedValues != null) { this.allowedValues = ImmutableSet.copyOf(option.allowedValues); } else { this.allowedValues = null; } Set accessors = newHashSet(); for (OptionMetadata other : options) { Preconditions.checkArgument(option.equals(other), "Conflicting options definitions: %s, %s", option, other); accessors.addAll(other.getAccessors()); } this.accessors = ImmutableSet.copyOf(accessors); } public OptionType getOptionType() { return optionType; } public Set getOptions() { return options; } public String getTitle() { return title; } public String getDescription() { return description; } public int getArity() { return arity; } public boolean isRequired() { return required; } public boolean isHidden() { return hidden; } public boolean isMultiValued() { return accessors.iterator().next().isMultiValued(); } public Class getJavaType() { return accessors.iterator().next().getJavaType(); } public Set getAccessors() { return accessors; } public Set getAllowedValues() { return allowedValues; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } OptionMetadata that = (OptionMetadata) o; if (arity != that.arity) { return false; } if (hidden != that.hidden) { return false; } if (required != that.required) { return false; } if (allowedValues != null ? !allowedValues.equals(that.allowedValues) : that.allowedValues != null) { return false; } if (description != null ? !description.equals(that.description) : that.description != null) { return false; } if (optionType != that.optionType) { return false; } if (!options.equals(that.options)) { return false; } if (!title.equals(that.title)) { return false; } return true; } @Override public int hashCode() { int result = optionType.hashCode(); result = 31 * result + options.hashCode(); result = 31 * result + title.hashCode(); result = 31 * result + (description != null ? description.hashCode() : 0); result = 31 * result + arity; result = 31 * result + (required ? 1 : 0); result = 31 * result + (hidden ? 1 : 0); result = 31 * result + (allowedValues != null ? allowedValues.hashCode() : 0); return result; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("OptionMetadata"); sb.append("{optionType=").append(optionType); sb.append(", options=").append(options); sb.append(", title='").append(title).append('\''); sb.append(", description='").append(description).append('\''); sb.append(", arity=").append(arity); sb.append(", required=").append(required); sb.append(", hidden=").append(hidden); sb.append(", accessors=").append(accessors); sb.append('}'); return sb.toString(); } public static Function> optionsGetter() { return new Function>() { public Set apply(OptionMetadata input) { return input.getOptions(); } }; } public static Predicate isHiddenPredicate() { return new Predicate() { @Override public boolean apply(@Nullable OptionMetadata input) { return !input.isHidden(); } }; } } airline-0.7/src/main/java/io/airlift/airline/model/SuggesterMetadata.java000066400000000000000000000014041242674174300265100ustar00rootroot00000000000000package io.airlift.airline.model; import com.google.common.collect.ImmutableList; import io.airlift.airline.Accessor; import io.airlift.airline.Suggester; import java.util.List; public class SuggesterMetadata { private final Class suggesterClass; private final List metadataInjections; public SuggesterMetadata(Class suggesterClass, List metadataInjections) { this.suggesterClass = suggesterClass; this.metadataInjections = ImmutableList.copyOf(metadataInjections); } public Class getSuggesterClass() { return suggesterClass; } public List getMetadataInjections() { return metadataInjections; } } airline-0.7/src/test/000077500000000000000000000000001242674174300145635ustar00rootroot00000000000000airline-0.7/src/test/java/000077500000000000000000000000001242674174300155045ustar00rootroot00000000000000airline-0.7/src/test/java/io/000077500000000000000000000000001242674174300161135ustar00rootroot00000000000000airline-0.7/src/test/java/io/airlift/000077500000000000000000000000001242674174300175455ustar00rootroot00000000000000airline-0.7/src/test/java/io/airlift/airline/000077500000000000000000000000001242674174300211705ustar00rootroot00000000000000airline-0.7/src/test/java/io/airlift/airline/ArgsRequiredWrongMain.java000066400000000000000000000002471242674174300262550ustar00rootroot00000000000000package io.airlift.airline; @Command(name = "ArgsRequiredWrongMain") public class ArgsRequiredWrongMain { @Arguments(required = true) public String[] file; } airline-0.7/src/test/java/io/airlift/airline/Git.java000066400000000000000000000042211242674174300225550ustar00rootroot00000000000000package io.airlift.airline; import io.airlift.airline.Cli.CliBuilder; import java.util.List; import static io.airlift.airline.OptionType.GLOBAL; public class Git { public static void main(String... args) { CliBuilder builder = Cli.builder("git") .withDescription("the stupid content tracker") .withDefaultCommand(Help.class) .withCommand(Help.class) .withCommand(Add.class); builder.withGroup("remote") .withDescription("Manage set of tracked repositories") .withDefaultCommand(RemoteShow.class) .withCommand(RemoteShow.class) .withCommand(RemoteAdd.class); Cli gitParser = builder.build(); gitParser.parse(args).run(); } public static class GitCommand implements Runnable { @Option(type = GLOBAL, name = "-v", description = "Verbose mode") public boolean verbose; public void run() { System.out.println(getClass().getSimpleName()); } } @Command(name = "add", description = "Add file contents to the index") public static class Add extends GitCommand { @Arguments(description = "Patterns of files to be added") public List patterns; @Option(name = "-i", description = "Add modified contents interactively.") public boolean interactive; } @Command(name = "show", description = "Gives some information about the remote ") public static class RemoteShow extends GitCommand { @Option(name = "-n", description = "Do not query remote heads") public boolean noQuery; @Arguments(description = "Remote to show") public String remote; } @Command(name = "add", description = "Adds a remote") public static class RemoteAdd extends GitCommand { @Option(name = "-t", description = "Track only a specific branch") public String branch; @Arguments(description = "Remote repository to add") public List remote; } } airline-0.7/src/test/java/io/airlift/airline/Ping.java000066400000000000000000000011431242674174300227270ustar00rootroot00000000000000package io.airlift.airline; import javax.inject.Inject; @Command(name = "ping", description = "network test utility") public class Ping { @Inject public HelpOption helpOption; @Option(name = {"-c", "--count"}, description = "Send count packets") public int count = 1; public static void main(String... args) { Ping ping = SingleCommand.singleCommand(Ping.class).parse(args); if (ping.helpOption.showHelpIfRequested()) { return; } ping.run(); } public void run() { System.out.println("Ping count: " + count); } } airline-0.7/src/test/java/io/airlift/airline/TestCommand.java000066400000000000000000000322141242674174300242530ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import io.airlift.airline.Cli.CliBuilder; import io.airlift.airline.args.Args1; import io.airlift.airline.args.Args2; import io.airlift.airline.args.ArgsArityString; import io.airlift.airline.args.ArgsBooleanArity; import io.airlift.airline.args.ArgsBooleanArity0; import io.airlift.airline.args.ArgsEnum; import io.airlift.airline.args.ArgsInherited; import io.airlift.airline.args.ArgsMultipleUnparsed; import io.airlift.airline.args.ArgsOutOfMemory; import io.airlift.airline.args.ArgsPrivate; import io.airlift.airline.args.ArgsRequired; import io.airlift.airline.args.ArgsSingleChar; import io.airlift.airline.args.Arity1; import io.airlift.airline.args.OptionsRequired; import io.airlift.airline.command.CommandAdd; import io.airlift.airline.command.CommandCommit; import io.airlift.airline.model.CommandMetadata; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import static com.google.common.base.Predicates.compose; import static com.google.common.base.Predicates.equalTo; import static com.google.common.collect.Iterables.find; import static io.airlift.airline.TestingUtil.singleCommandParser; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; public class TestCommand { @Test public void simpleArgs() throws ParseException { Args1 args = singleCommandParser(Args1.class).parse("Args1", "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4", "-groups", "unit", "a", "b", "c"); assertTrue(args.debug); assertEquals(args.verbose.intValue(), 2); assertEquals(args.groups, "unit"); assertEquals(args.parameters, Arrays.asList("a", "b", "c")); assertEquals(args.floa, 1.2f, 0.1f); assertEquals(args.doub, 1.3f, 0.1f); assertEquals(args.bigd, new BigDecimal("1.4")); } @Test public void equalsArgs() throws ParseException { Args1 args = singleCommandParser(Args1.class).parse("Args1", "-debug", "-log=2", "-float=1.2", "-double=1.3", "-bigdecimal=1.4", "-groups=unit", "a", "b", "c"); assertTrue(args.debug); assertEquals(args.verbose.intValue(), 2); assertEquals(args.groups, "unit"); assertEquals(args.parameters, Arrays.asList("a", "b", "c")); assertEquals(args.floa, 1.2f, 0.1f); assertEquals(args.doub, 1.3f, 0.1f); assertEquals(args.bigd, new BigDecimal("1.4")); } @Test public void classicGetoptArgs() throws ParseException { ArgsSingleChar args = singleCommandParser(ArgsSingleChar.class).parse("ArgsSingleChar", "-lg", "-dsn", "-pa-p", "-2f", "-z", "--Dfoo"); assertTrue(args.l); assertTrue(args.g); assertTrue(args.d); assertEquals(args.s, "n"); assertEquals(args.p, "a-p"); assertFalse(args.n); assertTrue(args.two); assertEquals(args.f, "-z"); assertFalse(args.z); assertEquals(args.dir, null); assertEquals(args.parameters, Arrays.asList("--Dfoo")); } @Test public void classicGetoptFailure() throws ParseException { ArgsSingleChar args = singleCommandParser(ArgsSingleChar.class).parse("ArgsSingleChar", "-lgX"); assertFalse(args.l); assertFalse(args.g); assertEquals(args.parameters, Arrays.asList("-lgX")); } /** * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"), * the usage will only display it once. */ @Test public void repeatedArgs() { Cli parser = singleCommandParser(Args1.class); CommandMetadata command = find(parser.getMetadata().getDefaultGroupCommands(), compose(equalTo("Args1"), CommandMetadata.nameGetter())); assertEquals(command.getAllOptions().size(), 8); } /** * Getting the description of a nonexistent command should throw an exception. */ @Test(expectedExceptions = ParseException.class, expectedExceptionsMessageRegExp = "No command specified") public void nonexistentCommandShouldThrow() { singleCommandParser(Args1.class).parse(); } /** * Getting the description of a nonexistent command should throw an exception. */ @Test(expectedExceptions = ParseException.class, expectedExceptionsMessageRegExp = "Command 'foo' not recognized") public void unrecognizedCommandShouldThrow() { singleCommandParser(Args1.class).parse("foo"); } /** * Required options with multiple names should work with all names. */ private void multipleNames(String option) { Args1 args = singleCommandParser(Args1.class).parse("Args1", option, "2"); assertEquals(args.verbose.intValue(), 2); } @Test public void multipleNames1() { multipleNames("-log"); } @Test public void multipleNames2() { multipleNames("-verbose"); } @Test public void arityString() { ArgsArityString args = singleCommandParser(ArgsArityString.class).parse("ArgsArityString", "-pairs", "pair0", "pair1", "rest"); assertEquals(args.pairs.size(), 2); assertEquals(args.pairs.get(0), "pair0"); assertEquals(args.pairs.get(1), "pair1"); assertEquals(args.rest.size(), 1); assertEquals(args.rest.get(0), "rest"); } @Test(expectedExceptions = ParseException.class) public void arity2Fail() { singleCommandParser(ArgsArityString.class).parse("ArgsArityString", "-pairs", "pair0"); } @Test(expectedExceptions = IllegalArgumentException.class) public void multipleUnparsedFail() { singleCommandParser(ArgsMultipleUnparsed.class).parse(); } @Test public void privateArgs() { ArgsPrivate args = singleCommandParser(ArgsPrivate.class).parse("ArgsPrivate", "-verbose", "3"); assertEquals(args.getVerbose().intValue(), 3); } private void argsBoolean1(String[] params, Boolean expected) { List values = ImmutableList.builder().add("ArgsBooleanArity").add(params).build(); ArgsBooleanArity args = singleCommandParser(ArgsBooleanArity.class).parse(values); assertEquals(args.debug, expected); } private void argsBoolean0(String[] params, Boolean expected) { List values = ImmutableList.builder().add("ArgsBooleanArity0").add(params).build(); ArgsBooleanArity0 args = singleCommandParser(ArgsBooleanArity0.class).parse(values); assertEquals(args.debug, expected); } @Test public void booleanArity1() { argsBoolean1(new String[] {}, Boolean.FALSE); argsBoolean1(new String[] {"-debug", "true"}, Boolean.TRUE); } @Test public void booleanArity0() { argsBoolean0(new String[] {}, Boolean.FALSE); argsBoolean0(new String[] {"-debug"}, Boolean.TRUE); } @Test(expectedExceptions = ParseException.class) public void badParameterShouldThrowParameter1Exception() { singleCommandParser(Args1.class).parse("Args1", "-log", "foo"); } @Test(expectedExceptions = ParseException.class) public void badParameterShouldThrowParameter2Exception() { singleCommandParser(Args1.class).parse("Args1", "-long", "foo"); } @Test public void listParameters() { Args2 a = singleCommandParser(Args2.class).parse("Args2", "-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"); assertEquals(a.verbose.intValue(), 2); assertEquals(a.groups, "unit"); assertEquals(a.hosts, Arrays.asList("host2")); assertEquals(a.parameters, Arrays.asList("a", "b", "c")); } @Test public void inheritance() { ArgsInherited args = singleCommandParser(ArgsInherited.class).parse("ArgsInherited", "-log", "3", "-child", "2"); assertEquals(args.child.intValue(), 2); assertEquals(args.log.intValue(), 3); } @Test public void negativeNumber() { Args1 a = singleCommandParser(Args1.class).parse("Args1", "-verbose", "-3"); assertEquals(a.verbose.intValue(), -3); } @Test(expectedExceptions = ParseException.class) public void requiredMainParameters() { singleCommandParser(ArgsRequired.class).parse("ArgsRequired"); } @Test(expectedExceptions = ParseException.class, expectedExceptionsMessageRegExp = ".*option.*missing.*") public void requiredOptions() { singleCommandParser(OptionsRequired.class).parse("OptionsRequired"); } @Test public void ignoresOptionalOptions() { singleCommandParser(OptionsRequired.class).parse("OptionsRequired", "--required", "foo"); } private void verifyCommandOrdering(String[] commandNames, Class... commands) { CliBuilder builder = Cli.builder("foo"); for (Class command : commands) { builder = builder.withCommand(command); } Cli parser = builder.build(); final List commandParsers = parser.getMetadata().getDefaultGroupCommands(); assertEquals(commandParsers.size(), commands.length); int i = 0; for (CommandMetadata commandParser : commandParsers) { assertEquals(commandParser.getName(), commandNames[i++]); } } @Test public void commandsShouldBeShownInOrderOfInsertion() { verifyCommandOrdering(new String[] {"add", "commit"}, CommandAdd.class, CommandCommit.class); verifyCommandOrdering(new String[] {"commit", "add"}, CommandCommit.class, CommandAdd.class); } @DataProvider public static Object[][] f() { return new Integer[][] { new Integer[] {3, 5, 1}, new Integer[] {3, 8, 1}, new Integer[] {3, 12, 2}, new Integer[] {8, 12, 2}, new Integer[] {9, 10, 1}, }; } @Test(expectedExceptions = ParseException.class) public void arity1Fail() { singleCommandParser(Arity1.class).parse("Arity1", "-inspect"); } @Test public void arity1Success1() { Arity1 arguments = singleCommandParser(Arity1.class).parse("Arity1", "-inspect", "true"); assertTrue(arguments.inspect); } @Test public void arity1Success2() { Arity1 arguments = singleCommandParser(Arity1.class).parse("Arity1", "-inspect", "false"); assertFalse(arguments.inspect); } @Test(expectedExceptions = ParseException.class, description = "Verify that the main parameter's type is checked to be a List") public void wrongMainTypeShouldThrow() { singleCommandParser(ArgsRequiredWrongMain.class).parse("f1", "f2"); } @Test(description = "This used to run out of memory") public void oom() { singleCommandParser(ArgsOutOfMemory.class).parse("ArgsOutOfMemory"); } @Test public void getParametersShouldNotNpe() { singleCommandParser(Args1.class).parse("Args1"); } private static final List V = Arrays.asList("a", "b", "c", "d"); @DataProvider public Object[][] variable() { return new Object[][] { new Object[] {0, V.subList(0, 0), V}, new Object[] {1, V.subList(0, 1), V.subList(1, 4)}, new Object[] {2, V.subList(0, 2), V.subList(2, 4)}, new Object[] {3, V.subList(0, 3), V.subList(3, 4)}, new Object[] {4, V.subList(0, 4), V.subList(4, 4)}, }; } @Test public void enumArgs() { ArgsEnum args = singleCommandParser(ArgsEnum.class).parse("ArgsEnum", "-choice", "ONE"); assertEquals(args.choice, ArgsEnum.ChoiceType.ONE); } @Test(expectedExceptions = ParseException.class) public void enumArgsFail() { singleCommandParser(ArgsEnum.class).parse("-choice", "A"); } @SuppressWarnings("UnusedDeclaration") @Test(expectedExceptions = ParseException.class) public void shouldThrowIfUnknownOption() { @Command(name = "A") class A { @Option(name = "-long") public long l; } singleCommandParser(A.class).parse("-lon", "32"); } } airline-0.7/src/test/java/io/airlift/airline/TestGalaxyCommandLineParser.java000066400000000000000000000327661242674174300274220ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Joiner; import com.google.common.collect.Lists; import io.airlift.airline.Cli.CliBuilder; import org.testng.annotations.Test; import javax.inject.Inject; import java.util.List; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.toStringHelper; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.OptionType.GLOBAL; public class TestGalaxyCommandLineParser { @Test public void test() { parse(); parse("help"); parse("help", "galaxy"); parse("help", "show"); parse("help", "install"); parse("help", "upgrade"); parse("help", "upgrade"); parse("help", "terminate"); parse("help", "start"); parse("help", "stop"); parse("help", "restart"); parse("help", "reset-to-actual"); parse("help", "ssh"); parse("help", "agent"); parse("help", "agent", "show"); parse("help", "agent", "add"); parse("--debug", "show", "-u", "b2", "--state", "r"); parse("--debug", "install", "com.proofpoint.discovery:discovery-server:1.1", "@discovery:general:1.0"); parse("--debug", "upgrade", "-u", "b2", "1.1", "@1.0"); parse("--debug", "upgrade", "-u", "b2", "1.1", "@1.0", "-s", "r"); parse("--debug", "terminate", "-u", "b2"); parse("--debug", "start", "-u", "b2"); parse("--debug", "stop", "-u", "b2"); parse("--debug", "restart", "-u", "b2"); parse("--debug", "reset-to-actual", "-u", "b2"); parse("--debug", "ssh"); parse("--debug", "ssh", "-u", "b2", "--state", "r", "tail -F var/log/launcher.log"); parse("--debug", "agent"); parse("--debug", "agent", "show"); parse("--debug", "agent", "add", "--count", "4", "t1.micro"); } private Cli createParser() { CliBuilder builder = Cli.builder("galaxy") .withDescription("cloud management system") .withDefaultCommand(HelpCommand.class) .withCommand(HelpCommand.class) .withCommand(ShowCommand.class) .withCommand(InstallCommand.class) .withCommand(UpgradeCommand.class) .withCommand(TerminateCommand.class) .withCommand(StartCommand.class) .withCommand(StopCommand.class) .withCommand(RestartCommand.class) .withCommand(SshCommand.class) .withCommand(ResetToActualCommand.class); builder.withGroup("agent") .withDescription("Manage agents") .withDefaultCommand(AgentShowCommand.class) .withCommand(AgentShowCommand.class) .withCommand(AgentAddCommand.class) .withCommand(AgentTerminateCommand.class); return builder.build(); } private void parse(String... args) { System.out.println("$ galaxy " + Joiner.on(" ").join(args)); GalaxyCommand command = createParser().parse(args); command.execute(); System.out.println(); } public static class GlobalOptions { @Option(type = GLOBAL, name = "--debug", description = "Enable debug messages") public boolean debug = false; @Option(type = GLOBAL, name = "--coordinator", description = "Galaxy coordinator host (overrides GALAXY_COORDINATOR)") public String coordinator = firstNonNull(System.getenv("GALAXY_COORDINATOR"), "http://localhost:64000"); @Override public String toString() { return toStringHelper(this) .add("debug", debug) .add("coordinator", coordinator) .toString(); } } public static class SlotFilter { @Option(name = {"-b", "--binary"}, description = "Select slots with a given binary") public List binary; @Option(name = {"-c", "--config"}, description = "Select slots with a given configuration") public List config; @Option(name = {"-i", "--host"}, description = "Select slots on the given host") public List host; @Option(name = {"-I", "--ip"}, description = "Select slots at the given IP address") public List ip; @Option(name = {"-u", "--uuid"}, description = "Select slot with the given UUID") public List uuid; @Option(name = {"-s", "--state"}, description = "Select 'r{unning}', 's{topped}' or 'unknown' slots") public List state; @Override public String toString() { return toStringHelper(this) .add("binary", binary) .add("config", config) .add("host", host) .add("ip", ip) .add("uuid", uuid) .add("state", state) .toString(); } } public static class AgentFilter { @Option(name = {"-i", "--host"}, description = "Select slots on the given host") public final List host = newArrayList(); @Option(name = {"-I", "--ip"}, description = "Select slots at the given IP address") public final List ip = newArrayList(); @Option(name = {"-u", "--uuid"}, description = "Select slot with the given UUID") public final List uuid = newArrayList(); @Option(name = {"-s", "--state"}, description = "Select 'r{unning}', 's{topped}' or 'unknown' slots") public final List state = newArrayList(); @Override public String toString() { return toStringHelper(this) .add("host", host) .add("ip", ip) .add("uuid", uuid) .add("state", state) .toString(); } } public static abstract class GalaxyCommand { @Inject public GlobalOptions globalOptions = new GlobalOptions(); public void execute() { System.out.println(this); } } @Command(name = "help", description = "Display help information about galaxy") public static class HelpCommand extends GalaxyCommand { @Inject public Help help; @Override public void execute() { help.call(); } } @Command(name = "show", description = "Show state of all slots") public static class ShowCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "install", description = "Install software in a new slot") public static class InstallCommand extends GalaxyCommand { @Option(name = {"--count"}, description = "Number of instances to install") public int count = 1; @Inject public final AgentFilter agentFilter = new AgentFilter(); @Arguments(usage = " @", description = "The binary and @configuration to install. The default packaging is tar.gz") public final List assignment = Lists.newArrayList(); @Override public String toString() { return toStringHelper(this) .add("count", count) .add("agentFilter", agentFilter) .add("assignment", assignment) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "upgrade", description = "Upgrade software in a slot") public static class UpgradeCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Arguments(usage = "[] [@]", description = "Version of the binary and/or @configuration") public final List versions = Lists.newArrayList(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("versions", versions) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "terminate", description = "Terminate (remove) a slot") public static class TerminateCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "start", description = "Start a server") public static class StartCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "stop", description = "Stop a server") public static class StopCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "restart", description = "Restart server") public static class RestartCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "reset-to-actual", description = "Reset slot expected state to actual") public static class ResetToActualCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "ssh", description = "ssh to slot installation") public static class SshCommand extends GalaxyCommand { @Inject public final SlotFilter slotFilter = new SlotFilter(); @Arguments(description = "Command to execute on the remote host") public String command; @Override public String toString() { return toStringHelper(this) .add("slotFilter", slotFilter) .add("command", command) .toString(); } } @Command(name = "add", description = "Provision a new agent") public static class AgentAddCommand extends GalaxyCommand { @Option(name = {"--count"}, description = "Number of agents to provision") public int count = 1; @Option(name = {"--availability-zone"}, description = "Availability zone to provision") public String availabilityZone; @Arguments(usage = "[]", description = "Instance type to provision") public String instanceType; @Override public String toString() { return toStringHelper(this) .add("count", count) .add("availabilityZone", availabilityZone) .add("instanceType", instanceType) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "show", description = "Show agent details") public static class AgentShowCommand extends GalaxyCommand { @Inject public final AgentFilter agentFilter = new AgentFilter(); @Override public String toString() { return toStringHelper(this) .add("agentFilter", agentFilter) .add("globalOptions", globalOptions) .toString(); } } @Command(name = "terminate", description = "Provision a new agent") public static class AgentTerminateCommand extends GalaxyCommand { @Arguments(title = "agent-id", description = "Agent to terminate", required = true) public String agentId; @Override public String toString() { return toStringHelper(this) .add("agentId", agentId) .add("globalOptions", globalOptions) .toString(); } } } airline-0.7/src/test/java/io/airlift/airline/TestGit.java000066400000000000000000000013311242674174300234140ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Joiner; import org.testng.annotations.Test; public class TestGit { @Test public void test() { // simple command parsing example git("add", "-p", "file"); git("remote", "add", "origin", "git@github.com:airlift/airline.git"); git("-v", "remote", "show", "origin"); // show help git(); git("help"); git("help", "git"); git("help", "add"); git("help", "remote"); git("help", "remote", "show"); } private void git(String... args) { System.out.println("$ git " + Joiner.on(' ').join(args)); Git.main(args); System.out.println(); } } airline-0.7/src/test/java/io/airlift/airline/TestHelp.java000066400000000000000000000467411242674174300235770ustar00rootroot00000000000000/* * Copyright (C) 2012 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import io.airlift.airline.Cli.CliBuilder; import io.airlift.airline.Git.Add; import io.airlift.airline.Git.RemoteAdd; import io.airlift.airline.Git.RemoteShow; import io.airlift.airline.args.Args1; import io.airlift.airline.args.Args2; import io.airlift.airline.args.ArgsArityString; import io.airlift.airline.args.ArgsBooleanArity; import io.airlift.airline.args.ArgsInherited; import io.airlift.airline.args.ArgsRequired; import io.airlift.airline.args.CommandHidden; import io.airlift.airline.args.OptionsHidden; import io.airlift.airline.args.OptionsRequired; import org.testng.annotations.Test; import static io.airlift.airline.SingleCommand.singleCommand; import static org.testng.Assert.assertEquals; @Test public class TestHelp { @Test public void testGit() { CliBuilder builder = Cli.builder("git") .withDescription("the stupid content tracker") .withDefaultCommand(Help.class) .withCommand(Help.class) .withCommand(Add.class); builder.withGroup("remote") .withDescription("Manage set of tracked repositories") .withDefaultCommand(RemoteShow.class) .withCommand(RemoteShow.class) .withCommand(RemoteAdd.class); Cli gitParser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(gitParser.getMetadata(), ImmutableList.of(), out); assertEquals(out.toString(), "usage: git [-v] []\n" + "\n" + "The most commonly used git commands are:\n" + " add Add file contents to the index\n" + " help Display help information\n" + " remote Manage set of tracked repositories\n" + "\n" + "See 'git help ' for more information on a specific command.\n"); out = new StringBuilder(); Help.help(gitParser.getMetadata(), ImmutableList.of("add"), out); assertEquals(out.toString(), "NAME\n" + " git add - Add file contents to the index\n" + "\n" + "SYNOPSIS\n" + " git [-v] add [-i] [--] [...]\n" + "\n" + "OPTIONS\n" + " -i\n" + " Add modified contents interactively.\n" + "\n" + " -v\n" + " Verbose mode\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + " Patterns of files to be added\n" + "\n"); out = new StringBuilder(); Help.help(gitParser.getMetadata(), ImmutableList.of("remote"), out); assertEquals(out.toString(), "NAME\n" + " git remote - Manage set of tracked repositories\n" + "\n" + "SYNOPSIS\n" + " git [-v] remote\n" + " git [-v] remote add [-t ]\n" + " git [-v] remote show [-n]\n" + "\n" + "OPTIONS\n" + " -v\n" + " Verbose mode\n" + "\n" + "COMMANDS\n" + " With no arguments, Gives some information about the remote \n" + "\n" + " show\n" + " Gives some information about the remote \n" + "\n" + " With -n option, Do not query remote heads\n" + "\n" + " add\n" + " Adds a remote\n" + "\n" + " With -t option, Track only a specific branch\n" + "\n"); } @Test public void testArgs1() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, Args1.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("Args1"), out); assertEquals(out.toString(), "NAME\n" + " test Args1 - args1 description\n" + "\n" + "SYNOPSIS\n" + " test Args1 [-bigdecimal ] [-date ] [-debug] [-double ]\n" + " [-float ] [-groups ]\n" + " [(-log | -verbose )] [-long ] [--]\n" + " [...]\n" + "\n" + "OPTIONS\n" + " -bigdecimal \n" + " A BigDecimal number\n" + "\n" + " -date \n" + " An ISO 8601 formatted date.\n" + "\n" + " -debug\n" + " Debug mode\n" + "\n" + " -double \n" + " A double number\n" + "\n" + " -float \n" + " A float number\n" + "\n" + " -groups \n" + " Comma-separated list of group names to be run\n" + "\n" + " -log , -verbose \n" + " Level of verbosity\n" + "\n" + " -long \n" + " A long number\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + "\n" + "\n"); } @Test public void testArgs2() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, Args2.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("Args2"), out); assertEquals(out.toString(), "NAME\n" + " test Args2 -\n" + "\n" + "SYNOPSIS\n" + " test Args2 [-debug] [-groups ] [-host ...]\n" + " [(-log | -verbose )] [--] [...]\n" + "\n" + "OPTIONS\n" + " -debug\n" + " Debug mode\n" + "\n" + " -groups \n" + " Comma-separated list of group names to be run\n" + "\n" + " -host \n" + " The host\n" + "\n" + " -log , -verbose \n" + " Level of verbosity\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + " List of parameters\n" + "\n"); } @Test public void testArgsAritySting() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, ArgsArityString.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("ArgsArityString"), out); assertEquals(out.toString(), "NAME\n" + " test ArgsArityString -\n" + "\n" + "SYNOPSIS\n" + " test ArgsArityString [-pairs ...] [--] [...]\n" + "\n" + "OPTIONS\n" + " -pairs \n" + " Pairs\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + " Rest\n" + "\n"); } @Test public void testArgsBooleanArity() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, ArgsBooleanArity.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("ArgsBooleanArity"), out); assertEquals(out.toString(), "NAME\n" + " test ArgsBooleanArity -\n" + "\n" + "SYNOPSIS\n" + " test ArgsBooleanArity [-debug ]\n" + "\n" + "OPTIONS\n" + " -debug \n" + "\n" + "\n"); } @Test public void testArgsInherited() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, ArgsInherited.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("ArgsInherited"), out); assertEquals(out.toString(), "NAME\n" + " test ArgsInherited -\n" + "\n" + "SYNOPSIS\n" + " test ArgsInherited [-child ] [-debug] [-groups ]\n" + " [-level ] [-log ] [--] [...]\n" + "\n" + "OPTIONS\n" + " -child \n" + " Child parameter\n" + "\n" + " -debug\n" + " Debug mode\n" + "\n" + " -groups \n" + " Comma-separated list of group names to be run\n" + "\n" + " -level \n" + " A long number\n" + "\n" + " -log \n" + " Level of verbosity\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + "\n" + "\n"); } @Test public void testArgsRequired() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, ArgsRequired.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("ArgsRequired"), out); assertEquals(out.toString(), "NAME\n" + " test ArgsRequired -\n" + "\n" + "SYNOPSIS\n" + " test ArgsRequired [--] ...\n" + "\n" + "OPTIONS\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + " List of files\n" + "\n"); } @Test public void testOptionsRequired() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, OptionsRequired.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("OptionsRequired"), out); assertEquals(out.toString(), "NAME\n" + " test OptionsRequired -\n" + "\n" + "SYNOPSIS\n" + " test OptionsRequired [--optional ]\n" + " --required \n" + "\n" + "OPTIONS\n" + " --optional \n" + "\n" + "\n" + " --required \n" + "\n" + "\n"); } @Test public void testOptionsHidden() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, OptionsHidden.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("OptionsHidden"), out); assertEquals(out.toString(), "NAME\n" + " test OptionsHidden -\n" + "\n" + "SYNOPSIS\n" + " test OptionsHidden [--optional ]\n" + "\n" + "OPTIONS\n" + " --optional \n" + "\n" + "\n"); } @Test public void testCommandHidden() { CliBuilder builder = Cli.builder("test") .withDescription("Test commandline") .withDefaultCommand(Help.class) .withCommands(Help.class, ArgsRequired.class, CommandHidden.class); Cli parser = builder.build(); StringBuilder out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of(), out); assertEquals(out.toString(), "usage: test []\n" + "\n" + "The most commonly used test commands are:\n" + " ArgsRequired\n" + " help Display help information\n" + "\n" + "See 'test help ' for more information on a specific command.\n"); out = new StringBuilder(); Help.help(parser.getMetadata(), ImmutableList.of("CommandHidden"), out); assertEquals(out.toString(), "NAME\n" + " test CommandHidden -\n" + "\n" + "SYNOPSIS\n" + " test CommandHidden [--optional ]\n" + "\n" + "OPTIONS\n" + " --optional \n" + "\n" + "\n"); } @Test public void testSingleCommandArgs1() { SingleCommand command = singleCommand(Args1.class); StringBuilder out = new StringBuilder(); new CommandUsage().usage(null, null, "test", command.getCommandMetadata(), out); assertEquals(out.toString(), "NAME\n" + " test - args1 description\n" + "\n" + "SYNOPSIS\n" + " test [-bigdecimal ] [-date ] [-debug] [-double ]\n" + " [-float ] [-groups ]\n" + " [(-log | -verbose )] [-long ] [--]\n" + " [...]\n" + "\n" + "OPTIONS\n" + " -bigdecimal \n" + " A BigDecimal number\n" + "\n" + " -date \n" + " An ISO 8601 formatted date.\n" + "\n" + " -debug\n" + " Debug mode\n" + "\n" + " -double \n" + " A double number\n" + "\n" + " -float \n" + " A float number\n" + "\n" + " -groups \n" + " Comma-separated list of group names to be run\n" + "\n" + " -log , -verbose \n" + " Level of verbosity\n" + "\n" + " -long \n" + " A long number\n" + "\n" + " --\n" + " This option can be used to separate command-line options from the\n" + " list of argument, (useful when arguments might be mistaken for\n" + " command-line options\n" + "\n" + " \n" + "\n" + "\n"); } } airline-0.7/src/test/java/io/airlift/airline/TestParametersDelegate.java000066400000000000000000000202541242674174300264340ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.collect.ImmutableList; import org.testng.annotations.Test; import javax.inject.Inject; import java.util.List; import static com.google.common.collect.Lists.newArrayList; import static io.airlift.airline.TestingUtil.singleCommandParser; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; /** * @author dain * @author rodionmoiseev */ public class TestParametersDelegate { @Command(name = "command") public static class DelegatingEmptyClassHasNoEffect { public static class EmptyDelegate { public String nonParamString = "a"; } @Option(name = "-a") public boolean isA; @Option(name = {"-b", "--long-b"}) public String bValue = ""; @Inject public EmptyDelegate delegate = new EmptyDelegate(); } @Test public void delegatingEmptyClassHasNoEffect() { DelegatingEmptyClassHasNoEffect p = Cli.builder("foo") .withCommand(DelegatingEmptyClassHasNoEffect.class) .build() .parse("command", "-a", "-b", "someValue"); assertTrue(p.isA); assertEquals(p.bValue, "someValue"); assertEquals(p.delegate.nonParamString, "a"); } // ======================================================================================================================== @Command(name = "command") public static class DelegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams { public static class ComplexDelegate { @Option(name = "-c") public boolean isC; @Option(name = {"-d", "--long-d"}) public Integer d; } @Option(name = "-a") public boolean isA; @Option(name = {"-b", "--long-b"}) public String bValue = ""; @Inject public ComplexDelegate delegate = new ComplexDelegate(); } @Test public void delegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams() { DelegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams p = singleCommandParser(DelegatingSetsFieldsOnBothMainParamsAndTheDelegatedParams.class) .parse("command", "-c", "--long-d", "123", "--long-b", "bValue"); assertFalse(p.isA); assertEquals(p.bValue, "bValue"); assertTrue(p.delegate.isC); assertEquals(p.delegate.d, Integer.valueOf(123)); } // ======================================================================================================================== @Command(name = "command") public static class CombinedAndNestedDelegates { public static class LeafDelegate { @Option(name = "--list") public List list = newArrayList("value1", "value2"); @Option(name = "--bool") public boolean bool; } public static class NestedDelegate1 { @Inject public LeafDelegate leafDelegate = new LeafDelegate(); @Option(name = {"-d", "--long-d"}) public Integer d; } public static class NestedDelegate2 { @Option(name = "-c") public boolean isC; @Inject public NestedDelegate1 nestedDelegate1 = new NestedDelegate1(); } @Option(name = "-a") public boolean isA; @Option(name = {"-b", "--long-b"}) public String bValue = ""; @Inject public NestedDelegate2 nestedDelegate2 = new NestedDelegate2(); } @Test public void combinedAndNestedDelegates() { CombinedAndNestedDelegates p = singleCommandParser(CombinedAndNestedDelegates.class) .parse("command", "-d", "234", "--list", "a", "--list", "b", "-a"); assertEquals(p.nestedDelegate2.nestedDelegate1.leafDelegate.list, newArrayList("value1", "value2", "a", "b")); assertFalse(p.nestedDelegate2.nestedDelegate1.leafDelegate.bool); assertEquals(p.nestedDelegate2.nestedDelegate1.d, Integer.valueOf(234)); assertFalse(p.nestedDelegate2.isC); assertTrue(p.isA); assertEquals(p.bValue, ""); } // ======================================================================================================================== @Command(name = "command") public static class CommandTest { public static class Delegate { @Option(name = "-a") public String a = "b"; } @Inject public Delegate delegate = new Delegate(); } @Test public void commandTest() { CommandTest c = singleCommandParser(CommandTest.class).parse("command", "-a", "a"); assertEquals(c.delegate.a, "a"); } // ======================================================================================================================== @Command(name = "command") public static class NullDelegatesAreProhibited { public static class ComplexDelegate { @Option(name = "-a") public boolean a; } @Inject public ComplexDelegate delegate; } @Test public void nullDelegatesAreAllowed() { NullDelegatesAreProhibited value = singleCommandParser(NullDelegatesAreProhibited.class).parse("command", "-a"); assertEquals(value.delegate.a, true); } // ======================================================================================================================== @Command(name = "command") public static class DuplicateDelegateAllowed { public static class Delegate { @Option(name = "-a") public String a; } @Inject public Delegate d1 = new Delegate(); @Inject public Delegate d2 = new Delegate(); } @Test public void duplicateDelegateAllowed() { DuplicateDelegateAllowed value = singleCommandParser(DuplicateDelegateAllowed.class).parse("command", "-a", "value"); assertEquals(value.d1.a, "value"); assertEquals(value.d2.a, "value"); } // ======================================================================================================================== @Command(name = "command") public static class DuplicateMainParametersAreAllowed { public static class Delegate1 { @Arguments public List mainParams1 = newArrayList(); } public static class Delegate2 { @Arguments public List mainParams1 = newArrayList(); } @Inject public Delegate1 delegate1 = new Delegate1(); @Inject public Delegate2 delegate2 = new Delegate2(); } @Test public void duplicateMainParametersAreAllowed() { DuplicateMainParametersAreAllowed value = singleCommandParser(DuplicateMainParametersAreAllowed.class).parse("command", "main", "params"); assertEquals(value.delegate1.mainParams1, ImmutableList.of("main", "params")); assertEquals(value.delegate2.mainParams1, ImmutableList.of("main", "params")); } // ======================================================================================================================== @SuppressWarnings("UnusedDeclaration") @Command(name = "command") public static class ConflictingMainParametersAreNotAllowed { public static class Delegate1 { @Arguments(description = "foo") public List mainParams1 = newArrayList(); } public static class Delegate2 { @Arguments(description = "bar") public List mainParams1 = newArrayList(); } @Inject public Delegate1 delegate1 = new Delegate1(); @Inject public Delegate2 delegate2 = new Delegate2(); } @Test(expectedExceptions = IllegalArgumentException.class) public void conflictingMainParametersAreNotAllowed() { singleCommandParser(ConflictingMainParametersAreNotAllowed.class).parse("command", "main", "params"); } } airline-0.7/src/test/java/io/airlift/airline/TestPing.java000066400000000000000000000010171242674174300235670ustar00rootroot00000000000000package io.airlift.airline; import com.google.common.base.Joiner; import org.testng.annotations.Test; public class TestPing { @Test public void test() { // simple command parsing example ping(); ping("-c", "5"); ping("--count", "9"); // show help ping("-h"); ping("--help"); } private void ping(String... args) { System.out.println("$ ping " + Joiner.on(' ').join(args)); Ping.main(args); System.out.println(); } } airline-0.7/src/test/java/io/airlift/airline/TestSingleCommand.java000066400000000000000000000306521242674174300254210ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline; import com.google.common.collect.ImmutableList; import io.airlift.airline.Cli.CliBuilder; import io.airlift.airline.args.Args1; import io.airlift.airline.args.Args2; import io.airlift.airline.args.ArgsArityString; import io.airlift.airline.args.ArgsBooleanArity; import io.airlift.airline.args.ArgsBooleanArity0; import io.airlift.airline.args.ArgsEnum; import io.airlift.airline.args.ArgsInherited; import io.airlift.airline.args.ArgsMultipleUnparsed; import io.airlift.airline.args.ArgsOutOfMemory; import io.airlift.airline.args.ArgsPrivate; import io.airlift.airline.args.ArgsRequired; import io.airlift.airline.args.ArgsSingleChar; import io.airlift.airline.args.Arity1; import io.airlift.airline.args.OptionsRequired; import io.airlift.airline.command.CommandAdd; import io.airlift.airline.command.CommandCommit; import io.airlift.airline.model.CommandMetadata; import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import javax.inject.Inject; import java.math.BigDecimal; import java.util.Arrays; import java.util.List; import static com.google.common.base.Predicates.compose; import static com.google.common.base.Predicates.equalTo; import static com.google.common.collect.Iterables.find; import static io.airlift.airline.SingleCommand.singleCommand; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; public class TestSingleCommand { @Test public void simpleArgs() throws ParseException { Args1 args = singleCommand(Args1.class).parse( "-debug", "-log", "2", "-float", "1.2", "-double", "1.3", "-bigdecimal", "1.4", "-groups", "unit", "a", "b", "c"); assertTrue(args.debug); assertEquals(args.verbose.intValue(), 2); assertEquals(args.groups, "unit"); assertEquals(args.parameters, Arrays.asList("a", "b", "c")); assertEquals(args.floa, 1.2f, 0.1f); assertEquals(args.doub, 1.3f, 0.1f); assertEquals(args.bigd, new BigDecimal("1.4")); } @Test public void equalsArgs() throws ParseException { Args1 args = singleCommand(Args1.class).parse( "-debug", "-log=2", "-float=1.2", "-double=1.3", "-bigdecimal=1.4", "-groups=unit", "a", "b", "c"); assertTrue(args.debug); assertEquals(args.verbose.intValue(), 2); assertEquals(args.groups, "unit"); assertEquals(args.parameters, Arrays.asList("a", "b", "c")); assertEquals(args.floa, 1.2f, 0.1f); assertEquals(args.doub, 1.3f, 0.1f); assertEquals(args.bigd, new BigDecimal("1.4")); } @Test public void classicGetoptArgs() throws ParseException { ArgsSingleChar args = singleCommand(ArgsSingleChar.class).parse( "-lg", "-dsn", "-pa-p", "-2f", "-z", "--Dfoo"); assertTrue(args.l); assertTrue(args.g); assertTrue(args.d); assertEquals(args.s, "n"); assertEquals(args.p, "a-p"); assertFalse(args.n); assertTrue(args.two); assertEquals(args.f, "-z"); assertFalse(args.z); assertEquals(args.dir, null); assertEquals(args.parameters, Arrays.asList("--Dfoo")); } @Test public void classicGetoptFailure() throws ParseException { ArgsSingleChar args = singleCommand(ArgsSingleChar.class).parse( "-lgX"); assertFalse(args.l); assertFalse(args.g); assertEquals(args.parameters, Arrays.asList("-lgX")); } /** * Make sure that if there are args with multiple names (e.g. "-log" and "-verbose"), * the usage will only display it once. */ @Test public void repeatedArgs() { SingleCommand parser = singleCommand(Args1.class); CommandMetadata command = find(ImmutableList.of(parser.getCommandMetadata()), compose(equalTo("Args1"), CommandMetadata.nameGetter())); assertEquals(command.getAllOptions().size(), 8); } /** * Required options with multiple names should work with all names. */ private void multipleNames(String option) { Args1 args = singleCommand(Args1.class).parse(option, "2"); assertEquals(args.verbose.intValue(), 2); } @Test public void multipleNames1() { multipleNames("-log"); } @Test public void multipleNames2() { multipleNames("-verbose"); } @Test public void arityString() { ArgsArityString args = singleCommand(ArgsArityString.class).parse("-pairs", "pair0", "pair1", "rest"); assertEquals(args.pairs.size(), 2); assertEquals(args.pairs.get(0), "pair0"); assertEquals(args.pairs.get(1), "pair1"); assertEquals(args.rest.size(), 1); assertEquals(args.rest.get(0), "rest"); } @Test(expectedExceptions = ParseException.class) public void arity2Fail() { singleCommand(ArgsArityString.class).parse("-pairs", "pair0"); } @Test(expectedExceptions = IllegalArgumentException.class) public void multipleUnparsedFail() { singleCommand(ArgsMultipleUnparsed.class).parse(); } @Test public void privateArgs() { ArgsPrivate args = singleCommand(ArgsPrivate.class).parse("-verbose", "3"); assertEquals(args.getVerbose().intValue(), 3); } private void argsBoolean1(String[] params, Boolean expected) { ArgsBooleanArity args = singleCommand(ArgsBooleanArity.class).parse(params); assertEquals(args.debug, expected); } private void argsBoolean0(String[] params, Boolean expected) { ArgsBooleanArity0 args = singleCommand(ArgsBooleanArity0.class).parse(params); assertEquals(args.debug, expected); } @Test public void booleanArity1() { argsBoolean1(new String[] {}, Boolean.FALSE); argsBoolean1(new String[] {"-debug", "true"}, Boolean.TRUE); } @Test public void booleanArity0() { argsBoolean0(new String[] {}, Boolean.FALSE); argsBoolean0(new String[] {"-debug"}, Boolean.TRUE); } @Test(expectedExceptions = ParseException.class) public void badParameterShouldThrowParameter1Exception() { singleCommand(Args1.class).parse("-log", "foo"); } @Test(expectedExceptions = ParseException.class) public void badParameterShouldThrowParameter2Exception() { singleCommand(Args1.class).parse("-long", "foo"); } @Test public void listParameters() { Args2 a = singleCommand(Args2.class).parse("-log", "2", "-groups", "unit", "a", "b", "c", "-host", "host2"); assertEquals(a.verbose.intValue(), 2); assertEquals(a.groups, "unit"); assertEquals(a.hosts, Arrays.asList("host2")); assertEquals(a.parameters, Arrays.asList("a", "b", "c")); } @Test public void inheritance() { ArgsInherited args = singleCommand(ArgsInherited.class).parse("-log", "3", "-child", "2"); assertEquals(args.child.intValue(), 2); assertEquals(args.log.intValue(), 3); } @Test public void negativeNumber() { Args1 a = singleCommand(Args1.class).parse("-verbose", "-3"); assertEquals(a.verbose.intValue(), -3); } @Test(expectedExceptions = ParseException.class) public void requiredMainParameters() { singleCommand(ArgsRequired.class).parse(); } @Test(expectedExceptions = ParseException.class, expectedExceptionsMessageRegExp = ".*option.*missing.*") public void requiredOptions() { singleCommand(OptionsRequired.class).parse(); } @Test public void ignoresOptionalOptions() { singleCommand(OptionsRequired.class).parse("--required", "foo"); } private void verifyCommandOrdering(String[] commandNames, Class... commands) { CliBuilder builder = Cli.builder("foo"); for (Class command : commands) { builder = builder.withCommand(command); } Cli parser = builder.build(); final List commandParsers = parser.getMetadata().getDefaultGroupCommands(); assertEquals(commandParsers.size(), commands.length); int i = 0; for (CommandMetadata commandParser : commandParsers) { assertEquals(commandParser.getName(), commandNames[i++]); } } @Test public void commandsShouldBeShownInOrderOfInsertion() { verifyCommandOrdering(new String[] {"add", "commit"}, CommandAdd.class, CommandCommit.class); verifyCommandOrdering(new String[] {"commit", "add"}, CommandCommit.class, CommandAdd.class); } @DataProvider public static Object[][] f() { return new Integer[][] { new Integer[] {3, 5, 1}, new Integer[] {3, 8, 1}, new Integer[] {3, 12, 2}, new Integer[] {8, 12, 2}, new Integer[] {9, 10, 1}, }; } @Test(expectedExceptions = ParseException.class) public void arity1Fail() { singleCommand(Arity1.class).parse("-inspect"); } @Test public void arity1Success1() { Arity1 arguments = singleCommand(Arity1.class).parse("-inspect", "true"); assertTrue(arguments.inspect); } @Test public void arity1Success2() { Arity1 arguments = singleCommand(Arity1.class).parse("-inspect", "false"); assertFalse(arguments.inspect); } @Test(expectedExceptions = ParseException.class, description = "Verify that the main parameter's type is checked to be a List") public void wrongMainTypeShouldThrow() { singleCommand(ArgsRequiredWrongMain.class).parse("f2"); } @Test(description = "This used to run out of memory") public void oom() { singleCommand(ArgsOutOfMemory.class).parse(); } @Test public void getParametersShouldNotNpe() { singleCommand(Args1.class).parse(); } private static final List V = Arrays.asList("a", "b", "c", "d"); @DataProvider public Object[][] variable() { return new Object[][] { new Object[] {0, V.subList(0, 0), V}, new Object[] {1, V.subList(0, 1), V.subList(1, 4)}, new Object[] {2, V.subList(0, 2), V.subList(2, 4)}, new Object[] {3, V.subList(0, 3), V.subList(3, 4)}, new Object[] {4, V.subList(0, 4), V.subList(4, 4)}, }; } @Test public void enumArgs() { ArgsEnum args = singleCommand(ArgsEnum.class).parse("-choice", "ONE"); assertEquals(args.choice, ArgsEnum.ChoiceType.ONE); } @Test(expectedExceptions = ParseException.class) public void enumArgsFail() { singleCommand(ArgsEnum.class).parse("A"); } @SuppressWarnings("UnusedDeclaration") @Test(expectedExceptions = ParseException.class) public void shouldThrowIfUnknownOption() { @Command(name = "A") class A { @Option(name = "-long") public long l; } singleCommand(A.class).parse("32"); } @Test public void testSingleCommandHelpOption() { CommandTest commandTest = singleCommand(CommandTest.class).parse("-h", "-i", "foo"); assertTrue(commandTest.helpOption.showHelpIfRequested()); } @Command(name = "test", description = "TestCommand") public static class CommandTest { @Inject public HelpOption helpOption; @Arguments(description = "Patterns of files to be added") public List patterns; @Option(name = "-i", description = "Interactive add mode") public Boolean interactive = false; } } airline-0.7/src/test/java/io/airlift/airline/TestingUtil.java000066400000000000000000000003731242674174300243110ustar00rootroot00000000000000package io.airlift.airline; public class TestingUtil { public static Cli singleCommandParser(Class commandClass) { return Cli.builder("parser") .withCommand(commandClass) .build(); } } airline-0.7/src/test/java/io/airlift/airline/args/000077500000000000000000000000001242674174300221245ustar00rootroot00000000000000airline-0.7/src/test/java/io/airlift/airline/args/Args1.java000066400000000000000000000035771242674174300237600ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import com.google.common.collect.Lists; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import java.math.BigDecimal; import java.util.Date; import java.util.List; @Command(name = "Args1", description = "args1 description") public class Args1 { @Arguments public List parameters = Lists.newArrayList(); @Option(name = {"-log", "-verbose"}, description = "Level of verbosity") public Integer verbose = 1; @Option(name = "-groups", description = "Comma-separated list of group names to be run") public String groups; @Option(name = "-debug", description = "Debug mode") public boolean debug = false; @Option(name = "-long", description = "A long number") public long l; @Option(name = "-double", description = "A double number") public double doub; @Option(name = "-float", description = "A float number") public float floa; @Option(name = "-bigdecimal", description = "A BigDecimal number") public BigDecimal bigd; @Option(name = "-date", description = "An ISO 8601 formatted date.") public Date date; } airline-0.7/src/test/java/io/airlift/airline/args/Args2.java000066400000000000000000000030421242674174300237440ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import java.util.List; import static com.google.common.collect.Lists.newArrayList; @Command(name = "Args2") public class Args2 { @Arguments(description = "List of parameters") public List parameters = com.google.common.collect.Lists.newArrayList(); @Option(name = {"-log", "-verbose"}, description = "Level of verbosity") public Integer verbose = 1; @Option(name = "-groups", description = "Comma-separated list of group names to be run") public String groups; @Option(name = "-debug", description = "Debug mode") public boolean debug = false; @Option(name = "-host", description = "The host") public List hosts = newArrayList(); } airline-0.7/src/test/java/io/airlift/airline/args/ArgsArityString.java000066400000000000000000000022411242674174300260620ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import java.util.List; /** * Test parameter arity. * * @author cbeust */ @Command(name = "ArgsArityString") public class ArgsArityString { @Option(name = "-pairs", arity = 2, description = "Pairs") public List pairs; @Arguments(description = "Rest") public List rest; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsBooleanArity.java000066400000000000000000000017271242674174300262030ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "ArgsBooleanArity") public class ArgsBooleanArity { @Option(name = "-debug", arity = 1) public Boolean debug = false; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsBooleanArity0.java000066400000000000000000000017311242674174300262560ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "ArgsBooleanArity0") public class ArgsBooleanArity0 { @Option(name = "-debug", arity = 0) public Boolean debug = false; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsDefault.java000066400000000000000000000025641242674174300251770ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import com.google.common.collect.Lists; import io.airlift.airline.Arguments; import io.airlift.airline.Option; import java.util.List; public class ArgsDefault { @Arguments public List parameters = Lists.newArrayList(); @Option(name = "-log", description = "Level of verbosity") public Integer log = 1; @Option(name = "-groups", description = "Comma-separated list of group names to be run") public String groups; @Option(name = "-debug", description = "Debug mode") public boolean debug = false; @Option(name = "-level", description = "A long number") public long level; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsEnum.java000066400000000000000000000021421242674174300245070ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; /** * Test enums. * * @author Adrian Muraru */ @Command(name = "ArgsEnum") public class ArgsEnum { public enum ChoiceType { ONE, TWO, THREE } @Option(name = "-choice", description = "Choice parameter") public ChoiceType choice = ChoiceType.ONE; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsInherited.java000066400000000000000000000017771242674174300255330ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "ArgsInherited") public class ArgsInherited extends ArgsDefault { @Option(name = "-child", description = "Child parameter") public Integer child = 1; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsMultipleUnparsed.java000066400000000000000000000022241242674174300271010ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Arguments; import io.airlift.airline.Command; /** * Error case if multiple unparsed (without a names attribute) arguments are defined. * * @author cbeust */ @Command(name = "ArgsMultipleUnparsed") public class ArgsMultipleUnparsed { @Arguments(description = "Bogus1") public String unparsed1; @Arguments(description = "Bogus2") public String unparsed2; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsOutOfMemory.java000066400000000000000000000007711242674174300260360ustar00rootroot00000000000000package io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "ArgsOutOfMemory") public class ArgsOutOfMemory { @Option(name = {"-p", "--pattern"}, description = "pattern used by 'tail'. See http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout and http://logback.qos.ch/manual/layouts.html#AccessPatternLayout") public String pattern; @Option(name = "-q", description = "Filler arg") public String filler; } airline-0.7/src/test/java/io/airlift/airline/args/ArgsPrivate.java000066400000000000000000000020101242674174300252070ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "ArgsPrivate") public class ArgsPrivate { @Option(name = "-verbose") private Integer verbose = 1; public Integer getVerbose() { return verbose; } } airline-0.7/src/test/java/io/airlift/airline/args/ArgsRequired.java000066400000000000000000000020641242674174300253660ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import java.util.List; @Command(name = "ArgsRequired") public class ArgsRequired { @Arguments(description = "List of files", required = true) public List parameters = com.google.common.collect.Lists.newArrayList(); } airline-0.7/src/test/java/io/airlift/airline/args/ArgsSingleChar.java000066400000000000000000000035351242674174300256310ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import com.google.common.collect.Lists; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import java.util.List; @Command(name = "ArgsSingleChar") public class ArgsSingleChar { @Arguments public List parameters = Lists.newArrayList(); @Option(name = {"-l"}, description = "Long") public boolean l = false; @Option(name = "-g", description = "Global") public boolean g = false; @Option(name = "-d", description = "Debug mode") public boolean d = false; @Option(name = "-s", description = "A string") public String s = null; @Option(name = "-p", description = "A path") public String p = null; @Option(name = "-n", description = "No action") public boolean n = false; @Option(name = "-2", description = "Two") public boolean two = false; @Option(name = "-f", description = "A filename") public String f = null; @Option(name = "-z", description = "Compress") public boolean z = false; @Option(name = "--D", description = "Directory") public String dir; } airline-0.7/src/test/java/io/airlift/airline/args/Arity1.java000066400000000000000000000004041242674174300241360ustar00rootroot00000000000000package io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "Arity1") public class Arity1 { @Option(arity = 1, name = "-inspect", description = "", required = false) public boolean inspect; } airline-0.7/src/test/java/io/airlift/airline/args/CommandHidden.java000066400000000000000000000020701242674174300254600ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "CommandHidden", hidden = true) public class CommandHidden { @Option(name = "--hidden", hidden = true) public String hiddenOption; @Option(name = "--optional", hidden = false) public String optionalOption; } airline-0.7/src/test/java/io/airlift/airline/args/CommandLineArgs.java000066400000000000000000000106141242674174300257740ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import java.util.List; @Command(name = "CommandLineArgs") public class CommandLineArgs { @Arguments(description = "The XML suite files to run") public List suiteFiles = com.google.common.collect.Lists.newArrayList(); @Option(name = {"-log", "-verbose"}, description = "Level of verbosity") public Integer verbose; @Option(name = "-groups", description = "Comma-separated list of group names to be run") public String groups; @Option(name = "-excludedgroups", description = "Comma-separated list of group names to be " + "run") public String excludedGroups; @Option(name = "-d", description = "Output directory") public String outputDirectory; @Option(name = "-junit", description = "JUnit mode") public Boolean junit = Boolean.FALSE; @Option(name = "-listener", description = "List of .class files or list of class names" + " implementing ITestListener or ISuiteListener") public String listener; @Option(name = "-methodselectors", description = "List of .class files or list of class " + "names implementing IMethodSelector") public String methodSelectors; @Option(name = "-objectfactory", description = "List of .class files or list of class " + "names implementing ITestRunnerFactory") public String objectFactory; @Option(name = "-parallel", description = "Parallel mode (methods, tests or classes)") public String parallelMode; @Option(name = "-configfailurepolicy", description = "Configuration failure policy (skip or continue)") public String configFailurePolicy; @Option(name = "-threadcount", description = "Number of threads to use when running tests " + "in parallel") public Integer threadCount; @Option(name = "-dataproviderthreadcount", description = "Number of threads to use when " + "running data providers") public Integer dataProviderThreadCount; @Option(name = "-suitename", description = "Default name of test suite, if not specified " + "in suite definition file or source code") public String suiteName; @Option(name = "-testname", description = "Default name of test, if not specified in suite" + "definition file or source code") public String testName; @Option(name = "-reporter", description = "Extended configuration for custom report listener") public String reporter; /** * Used as map key for the complete list of report listeners provided with the above argument */ @Option(name = "-reporterslist") public String reportersList; @Option(name = "-usedefaultlisteners", description = "Whether to use the default listeners") public String useDefaultListeners = "true"; @Option(name = "-skipfailedinvocationcounts") public Boolean skipFailedInvocationCounts; @Option(name = "-testclass", description = "The list of test classes") public String testClass; @Option(name = "-testnames", description = "The list of test names to run") public String testNames; @Option(name = "-testjar", description = "") public String testJar; @Option(name = "-testRunFactory", description = "") public String testRunFactory; @Option(name = "-port", description = "The port") public Integer port; @Option(name = "-host", description = "The host") public String host; @Option(name = "-master", description = "Host where the master is") public String master; @Option(name = "-slave", description = "Host where the slave is") public String slave; } airline-0.7/src/test/java/io/airlift/airline/args/OptionsHidden.java000066400000000000000000000020511242674174300255340ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "OptionsHidden") public class OptionsHidden { @Option(name = "--hidden", hidden = true) public String hiddenOption; @Option(name = "--optional", hidden = false) public String optionalOption; } airline-0.7/src/test/java/io/airlift/airline/args/OptionsRequired.java000066400000000000000000000020651242674174300261260ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.args; import io.airlift.airline.Command; import io.airlift.airline.Option; @Command(name = "OptionsRequired") public class OptionsRequired { @Option(name = "--required", required = true) public String requiredOption; @Option(name = "--optional", required = false) public String optionalOption; } airline-0.7/src/test/java/io/airlift/airline/command/000077500000000000000000000000001242674174300226065ustar00rootroot00000000000000airline-0.7/src/test/java/io/airlift/airline/command/CommandAdd.java000066400000000000000000000023321242674174300254400ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.command; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import javax.inject.Inject; import java.util.List; @Command(name = "add", description = "Add file contents to the index") public class CommandAdd { @Inject public CommandMain commandMain; @Arguments(description = "Patterns of files to be added") public List patterns; @Option(name = "-i") public Boolean interactive = false; } airline-0.7/src/test/java/io/airlift/airline/command/CommandCommit.java000066400000000000000000000024371242674174300262060ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.command; import io.airlift.airline.Arguments; import io.airlift.airline.Command; import io.airlift.airline.Option; import javax.inject.Inject; import java.util.List; @Command(name = "commit", description = "Record changes to the repository") public class CommandCommit { @Inject public CommandMain commandMain; @Arguments(description = "List of files") public List files; @Option(name = "--amend", description = "Amend") public Boolean amend = false; @Option(name = "--author") public String author; } airline-0.7/src/test/java/io/airlift/airline/command/CommandMain.java000066400000000000000000000017431242674174300256410ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.command; import io.airlift.airline.Option; import static io.airlift.airline.OptionType.GLOBAL; public class CommandMain { @Option(type = GLOBAL, name = "-v", description = "Verbose mode") public Boolean verbose = false; } airline-0.7/src/test/java/io/airlift/airline/command/CommandTest.java000066400000000000000000000050551242674174300256740ustar00rootroot00000000000000/** * Copyright (C) 2010 the original author or authors. * See the notice.md file distributed with this work for additional * information regarding copyright ownership. * * 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 io.airlift.airline.command; import io.airlift.airline.Cli; import org.testng.annotations.Test; import java.util.Arrays; import static io.airlift.airline.TestingUtil.singleCommandParser; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; public class CommandTest { @Test public void namedCommandTest1() { Cli parser = Cli.builder("git") .withCommand(CommandAdd.class) .withCommand(CommandCommit.class) .build(); Object command = parser.parse("add", "-i", "A.java"); assertNotNull(command, "command is null"); assertTrue(command instanceof CommandAdd); CommandAdd add = (CommandAdd) command; assertEquals(add.interactive.booleanValue(), true); assertEquals(add.patterns, Arrays.asList("A.java")); } @Test(expectedExceptions = IllegalArgumentException.class) public void shouldComplainIfNoAnnotations() { singleCommandParser(String.class); } @Test public void commandTest2() { Cli parser = Cli.builder("git") .withCommand(CommandAdd.class) .withCommand(CommandCommit.class) .build(); parser.parse("-v", "commit", "--amend", "--author", "cbeust", "A.java", "B.java"); Object command = parser.parse("-v", "commit", "--amend", "--author", "cbeust", "A.java", "B.java"); assertNotNull(command, "command is null"); assertTrue(command instanceof CommandCommit); CommandCommit commit = (CommandCommit) command; assertTrue(commit.commandMain.verbose); assertTrue(commit.amend); assertEquals(commit.author, "cbeust"); assertEquals(commit.files, Arrays.asList("A.java", "B.java")); } }