Skip to content

Commit 6d6dde5

Browse files
committed
Merge branch 'develop' for 5.1.2
2 parents 9810fcb + 0b0246e commit 6d6dde5

25 files changed

+1292
-785
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
# Ant files
2-
/nbproject/private/
1+
# Build files
32
/build/
43
/dist/
54
/target/
65
manifest.mf
6+
.flattened-pom.xml
77

88
# Backup files
99
*~

CONTRIBUTING.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Contributing to OLCUT
2+
3+
We welcome your contributions! There are several directions that OLCUT could
4+
be extended in. New config file formats could be added, more classes can be
5+
added for the `@Config` annotation, more classes can be supported by the
6+
`CommandInterpreter`'s `@Command` methods. The remote component discovery based on
7+
Jini / Apache River could stand to be modernized. Have an idea? Talk to us and
8+
let's make it happen. Just file an Issue to get in touch.
9+
10+
## Questions, Bugs, and Features
11+
12+
If you hit a bug or have an enhancement request please file a [GitHub issue](https://github.com/oracle/olcut/issues).
13+
This is also a great way to ask us questions. When filing a bug remember that
14+
the better written the bug is, the more likely it is to be fixed. Please include:
15+
16+
1. Version of OLCUT
17+
1. OS
18+
1. Java version
19+
1. As much code as you can provide to reproduce the bug
20+
1. Any relevant configuration file snippets
21+
1. Steps to reproduce
22+
23+
## Code
24+
25+
We welcome code contributions, but we need the contributor to sign the
26+
[Oracle Contributor Agreement (OCA)](https://www.oracle.com/technetwork/community/oca-486395.html)
27+
first.
28+
29+
The process:
30+
31+
1. We encourage you to file an [issue](https://github.com/oracle/olcut/issues) to discuss your idea with us before implementing anything.
32+
1. Sign the [OCA](https://www.oracle.com/technetwork/community/oca-486395.html)
33+
1. Fork the repository and work your magic
34+
1. Create a Pull Request
35+
1. We will review your PR and merge as appropriate.
36+
37+
## Code of Conduct
38+
39+
Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If
40+
you'd like more specific guidelines see the
41+
[Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html)

README-Commands.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
# Command Interpreter
2+
3+
OLCUT provides a Command Interpreter that can be used for invoking or interacting
4+
with your software. It can be used as a test harness to poke and prod different
5+
parts of your code, or can be used inside a JVM that is also running other
6+
pieces of software that you'd like to monitor or in some other way interact
7+
with. It is also a great way to build simple shell-based utilities without having
8+
to make a million different main classes or command line arguments.
9+
10+
The `CommandInterpreter` has a number of built-in commands for things like shell
11+
history, status, file redirection, running a script of commands, etc. These
12+
commands are all grouped together for convenience, meaning they'll appear
13+
together when you run "help".
14+
15+
The gnu-readline capability in the `CommandInterpreter` is provided by the
16+
[jline3 library](https://github.com/jline/jline3). It offers native readline-style
17+
support (including history and tab completion) on the following platforms: Solaris,
18+
Linux, OS X, FreeBSD, and Windows. While OLCUT is 100% Java, JLine does require
19+
a few native bits to run the Command Interpreter properly on these platforms.
20+
21+
## Defining commands
22+
23+
To add your own commands to the shell you simply define and annotate methods
24+
that take a `CommandInterpreter` as their first argument and supported types
25+
as any additional arguments. Any object containing commands must implement
26+
the `CommandGroup` interface. All commands contained within the object will
27+
be put together in the same group.
28+
29+
Defining a command looks like this
30+
31+
```java
32+
public class Processor implements CommandGroup {
33+
protected int numProcessed = 0;
34+
35+
public String getName() {
36+
return "Process";
37+
}
38+
39+
public String getDescription() {
40+
return "Commands for the Oracle Labs Sound Processor";
41+
}
42+
43+
@Command(usage="<fileName> - filter the given file")
44+
public String filter(CommandInterpreter ci, File file) {
45+
pipeline.filter(file);
46+
ci.out.println("Processing " + file.getName());
47+
numProcessed++;
48+
return "";
49+
}
50+
51+
@Command(usage="Prints stats for this processor")
52+
public String printStats(CommandInterpreter ci) {
53+
return "numProcessed: " + numProcessed;
54+
}
55+
}
56+
```
57+
58+
Then you can create and start a shell with that command in your main program,
59+
perhaps after having loaded any relevant configuration.
60+
61+
```java
62+
Processor p = new Processor(pipelineInstance);
63+
CommandInterpreter shell = new CommandInterpreter();
64+
shell.add(p);
65+
shell.run();
66+
```
67+
68+
Your main thread will block in a read/eval/print loop at this point.
69+
`CommandInterpreter` may also be run as a separate thread by invoking `start()`.
70+
71+
Any number of parameters may be given to a `Command`. The `CommandInterpreter` will
72+
check the number provided and give appropriate error messages including the
73+
usage string in the `@Command` annotation. It will parse the arguments and convert
74+
them to the appropriate types and invoke the Command if possible. Supported
75+
argument types are:
76+
77+
* `String`
78+
* `Integer`, `int`
79+
* `Long`, `long`
80+
* `Double`, `double`
81+
* `Float`, `float`
82+
* `Boolean`, `boolean`
83+
* `File`
84+
* `Enum` of any type
85+
* `String[]` (as the only parameter type)
86+
87+
Note that there is a one-to-one correspondence between method names and
88+
`Commands`. No distinctions are made for method signatures with different
89+
parameters. No guarantee is made for the behavior of a shell with multiple
90+
methods that have the same name (See Layered Command Interpreter below).
91+
The `CommandInterpreter` instance that is passed in will be the currently
92+
running shell. It is a best practice to use the shell's output (`ci.out`) for writing
93+
output so that output can be easily redirected to any output stream the
94+
shell may be embedded in.
95+
96+
## Optional parameters
97+
98+
For more flexibility in your commands, you may specify that trailing method
99+
parameters be optional. Any optional parameter must have a default value
100+
assigned to it, expressed as a string that would be entered on the command
101+
line. The default value may be the text "null" if you choose, although this
102+
should only be used with object/reference types and not base types. Optional
103+
parameters are tagged with the `@Optional` annotation. The above filter method
104+
could take an optional parameter to specify where the output of the pipeline
105+
should go:
106+
107+
```java
108+
@Command(usage="<inFile> [<outFile>] - run this filter on a file")
109+
public String filter(CommandInterpreter ci,
110+
File inFile,
111+
@Optional(val="/tmp/output.au", File outFile) {
112+
pipeline.filter(inFile, outFile);
113+
return "";
114+
}
115+
```
116+
117+
## Tab Completion
118+
119+
Commands added to a `CommandInterpreter` may provide tab completion for each of
120+
their arguments. A separate method may be used to specify how each argument should
121+
be completed. The method returns an array of `Completer` objects (described below),
122+
one per argument, and takes no parameters. These methods may either be associated
123+
in the `@Command` annotation (described below), or can follow a naming convention
124+
to be paired with the method they provide completers for. The following example
125+
method would be used to find completers for the `filter` command:
126+
127+
```java
128+
public Completer[] filterCompleters() {
129+
return new Completer[]{
130+
new FileNameCompleter(),
131+
new FileNameCompleter()
132+
};
133+
}
134+
```
135+
136+
In this example, an array of completers is returned, one per argument. This
137+
could actually be simplified because the behavior of the completers is to
138+
reuse the last completer in the array for all further arguments. Simply
139+
providing a single `FileNameCompleter` would work the same for this method. To
140+
prevent tab-completion for a particular parameter, or to prevent the last
141+
completer from repeating, place a `NullCompleter` in the array in the appropriate
142+
spot.
143+
144+
The following types of completers are available in OLCUT. These are provided
145+
by the [jline library](https://github.com/jline/jline3).
146+
147+
* `FileNameCompleter` - Completes file names starting in the PWD
148+
* `StringsCompleter` - Pass it a list of strings or a Supplier to give it the values it should complete to
149+
* `EnumCompleter` - Completes with values from a specified Enum type
150+
* `NullCompleter` - Does not complete with anything.
151+
* `IntCompleter` - Just kidding.
152+
153+
If you wish to reuse a method that generates completers, you can use an
154+
attribute of the `@Command` annotation to specify the name of the completer method
155+
to use instead of relying on the `xxxCompleters` convention. For example,
156+
if multiple commands take a single File parameter, you might make a method such
157+
as this one:
158+
159+
```java
160+
public Completers[] fileCompleter() {
161+
return new Completer[]{
162+
new FileNameCompleter(),
163+
new NullCompleter()
164+
}
165+
}
166+
```
167+
168+
Then when annotating a method that takes a `File` as its parameter, you would
169+
specify:
170+
171+
```java
172+
@Command(usage="Processes a single file",
173+
completers="fileCompleter")
174+
```
175+
176+
Multiple annotated commands may share the same completer method in this way.
177+
178+
## Manual argument processing
179+
180+
In some cases, taking the supported types as arguments doesn't provide enough
181+
flexibility for the way you want your command to work. In this case, you can
182+
instead have your method use `String[]` as its second parameter (after the
183+
`CommandInterpreter`) and all arguments provided in the shell will be passed
184+
through verbatim to allow you to do your own argument parsing. This is
185+
particularly useful if you want to support a varargs-style syntax. You may
186+
still provide `Completer`s even if the arguments are not otherwise specified.
187+
188+
## Layered Command Interpreter (Mixing in more commands)
189+
190+
Sometimes when building a large shell, you may have multiple `CommandGroup`s that
191+
provide commands with the same name. To avoid namespace collisions, you can add
192+
your commands to a layer, then add the layer to the top-level shell.
193+
194+
```java
195+
CommandInterpreter shell = new CommandInterpreter();
196+
LayeredCommandInterpreter lci = new LayeredCommandInterpreter("pipe", "Pipeline Commands");
197+
lci.add(processor);
198+
shell.add(lci);
199+
```
200+
201+
To avoid ambiguity, all commands defined in `processor` can now be referred to
202+
with a ".pipe" extension (e.g. `filter.pipe`), but may also be used without any
203+
extension when there is no conflict.

0 commit comments

Comments
 (0)