blob: eece61abbcb4e6cc8a1decefda0dddf4a5a748cb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.felix.gogo.jline;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Function;
import org.apache.felix.service.command.Process;
import org.jline.builtins.Options;
public class Procedural {
static final String[] functions = {"each", "if", "not", "throw", "try", "until", "while", "break", "continue"};
public Object _main(CommandSession session, Object[] argv) throws Throwable {
if (argv == null || argv.length < 1) {
throw new IllegalArgumentException();
}
Process process = Process.Utils.current();
try {
return run(session, process, argv);
} catch (OptionException e) {
process.err().println(e.getMessage());
process.error(2);
} catch (HelpException e) {
process.err().println(e.getMessage());
process.error(0);
} catch (ThrownException e) {
process.error(1);
throw e.getCause();
}
return null;
}
@SuppressWarnings("serial")
protected static class OptionException extends Exception {
public OptionException(String message, Throwable cause) {
super(message, cause);
}
}
@SuppressWarnings("serial")
protected static class HelpException extends Exception {
public HelpException(String message) {
super(message);
}
}
@SuppressWarnings("serial")
protected static class ThrownException extends Exception {
public ThrownException(Throwable cause) {
super(cause);
}
}
@SuppressWarnings("serial")
protected static class BreakException extends Exception {
}
@SuppressWarnings("serial")
protected static class ContinueException extends Exception {
}
protected Options parseOptions(CommandSession session, String[] usage, Object[] argv) throws HelpException, OptionException {
try {
Options opt = Options.compile(usage, s -> get(session, s)).parse(argv, true);
if (opt.isSet("help")) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
opt.usage(new PrintStream(baos));
throw new HelpException(baos.toString());
}
return opt;
} catch (IllegalArgumentException e) {
throw new OptionException(e.getMessage(), e);
}
}
protected String get(CommandSession session, String name) {
Object o = session.get(name);
return o != null ? o.toString() : null;
}
protected Object run(CommandSession session, Process process, Object[] argv) throws Throwable {
switch (argv[0].toString()) {
case "each":
return doEach(session, process, argv);
case "if":
return doIf(session, process, argv);
case "not":
return doNot(session, process, argv);
case "throw":
return doThrow(session, process, argv);
case "try":
return doTry(session, process, argv);
case "until":
return doUntil(session, process, argv);
case "while":
return doWhile(session, process, argv);
case "break":
return doBreak(session, process, argv);
case "continue":
return doContinue(session, process, argv);
default:
throw new UnsupportedOperationException();
}
}
protected List<Object> doEach(CommandSession session,
Process process,
Object[] argv) throws Exception {
String[] usage = {
"each - loop over the elements",
"Usage: each [-r] elements [do] { closure }",
" elements an array to iterate on",
" closure a closure to call",
" -? --help Show help",
" -r --result Return a list containing each iteration result",
};
Options opt = parseOptions(session, usage, argv);
Collection<Object> elements = getElements(opt);
if (opt.argObjects().size() > 0 && "do".equals(opt.argObjects().get(0))) {
opt.argObjects().remove(0);
}
List<Function> functions = getFunctions(opt);
if (elements == null || functions == null || functions.size() != 1) {
process.err().println("usage: each elements [do] { closure }");
process.err().println(" elements: an array to iterate on");
process.err().println(" closure: a function or closure to call");
process.error(2);
return null;
}
List<Object> args = new ArrayList<>();
List<Object> results = new ArrayList<>();
args.add(null);
for (Object x : elements) {
checkInterrupt();
args.set(0, x);
try {
results.add(functions.get(0).execute(session, args));
} catch (BreakException b) {
break;
} catch (ContinueException c) {
// Ignore
}
}
return opt.isSet("result") ? results : null;
}
protected Object doIf(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"if - if / then / else construct",
"Usage: if {condition} [then] {if-action} [elif {cond} [then] {elif-action}]... [else] {else-action}",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
List<Function> conditions = new ArrayList<>();
List<Function> actions = new ArrayList<>();
Function elseFunction = null;
int step = 0;
boolean error = false;
for (Object obj : opt.argObjects()) {
switch (step) {
case 0:
if (obj instanceof Function) {
conditions.add((Function) obj);
} else {
error = true;
}
step = 1;
break;
case 1:
if ("then".equals(obj)) {
step = 2;
break;
}
case 2:
if (obj instanceof Function) {
actions.add((Function) obj);
step = 3;
} else {
error = true;
}
break;
case 3:
if ("elif".equals(obj)) {
step = 4;
} else if ("else".equals(obj)) {
step = 7;
} else if (obj instanceof Function) {
elseFunction = (Function) obj;
step = 8;
} else {
error = true;
}
break;
case 4:
if (obj instanceof Function) {
conditions.add((Function) obj);
} else {
error = true;
}
step = 5;
break;
case 5:
if ("then".equals(obj)) {
step = 6;
break;
}
case 6:
if (obj instanceof Function) {
actions.add((Function) obj);
step = 3;
} else {
error = true;
}
break;
case 7:
if (obj instanceof Function) {
elseFunction = (Function) obj;
step = 8;
} else {
error = true;
}
break;
case 8:
error = true;
break;
}
if (error) {
break;
}
}
error |= conditions.isEmpty();
error |= conditions.size() != actions.size();
if (error) {
process.err().println("usage: if {condition} [then] {if-action} [elif {elif-action}]... [else] {else-action}");
process.error(2);
return null;
}
for (int i = 0, length = conditions.size(); i < length; ++i) {
if (isTrue(session, conditions.get(i))) {
return actions.get(i).execute(session, null);
}
}
if (elseFunction != null) {
return elseFunction.execute(session, null);
}
return null;
}
protected Boolean doNot(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"not - return the opposite condition",
"Usage: not { condition }",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
List<Function> functions = getFunctions(opt);
if (functions == null || functions.size() != 1) {
process.err().println("usage: not { condition }");
process.error(2);
return null;
}
return !isTrue(session, functions.get(0));
}
protected Object doThrow(CommandSession session, Process process, Object[] argv) throws ThrownException, HelpException, OptionException {
String[] usage = {
"throw - throw an exception",
"Usage: throw [ message [ cause ] ]",
" throw exception",
" throw",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
if (opt.argObjects().size() == 0) {
Object exception = session.get("exception");
if (exception instanceof Throwable)
throw new ThrownException((Throwable) exception);
else
throw new ThrownException(new Exception());
}
else if (opt.argObjects().size() == 1 && opt.argObjects().get(0) instanceof Throwable) {
throw new ThrownException((Throwable) opt.argObjects().get(0));
}
else {
String message = opt.argObjects().get(0).toString();
Throwable cause = null;
if (opt.argObjects().size() > 1) {
if (opt.argObjects().get(1) instanceof Throwable) {
cause = (Throwable) opt.argObjects().get(1);
}
}
throw new ThrownException(new Exception(message, cause));
}
}
protected Object doTry(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"try - try / catch / finally construct",
"Usage: try { try-action } [ [catch] { catch-action } [ [finally] { finally-action } ] ]",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
Function tryAction = null;
Function catchFunction = null;
Function finallyFunction = null;
int step = 0;
boolean error = false;
for (Object obj : opt.argObjects()) {
if (tryAction == null) {
if (obj instanceof Function) {
tryAction = (Function) obj;
} else {
error = true;
break;
}
step = 1;
} else if ("catch".equals(obj)) {
if (step != 1) {
error = true;
break;
}
step = 2;
} else if ("finally".equals(obj)) {
if (step != 1 && step != 3) {
error = true;
break;
}
step = 4;
} else if (step == 1 || step == 2) {
if (obj instanceof Function) {
catchFunction = (Function) obj;
} else {
error = true;
break;
}
step = 3;
} else if (step == 3 || step == 4) {
if (obj instanceof Function) {
finallyFunction = (Function) obj;
} else {
error = true;
break;
}
step = 5;
} else {
error = true;
break;
}
}
error |= tryAction == null;
error |= catchFunction == null && finallyFunction == null;
if (error) {
process.err().println("usage: try { try-action } [ [catch] { catch-action } [ [finally] { finally-action } ] ]");
process.error(2);
return null;
}
try {
return tryAction.execute(session, null);
} catch (BreakException b) {
throw b;
} catch (Exception e) {
session.put("exception", e);
if (catchFunction != null) {
catchFunction.execute(session, null);
}
return null;
} finally {
if (finallyFunction != null) {
finallyFunction.execute(session, null);
}
}
}
protected Object doWhile(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"while - while loop",
"Usage: while { condition } [do] { action }",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
Function condition = null;
Function action = null;
int step = 0;
boolean error = false;
for (Object obj : opt.argObjects()) {
if (condition == null) {
if (obj instanceof Function) {
condition = (Function) obj;
} else {
error = true;
break;
}
step = 1;
} else if ("do".equals(obj)) {
if (step != 1) {
error = true;
break;
}
step = 2;
} else if (step == 1 || step == 2) {
if (obj instanceof Function) {
action = (Function) obj;
} else {
error = true;
break;
}
step = 3;
} else {
error = true;
break;
}
}
error |= condition == null;
error |= action == null;
if (error) {
process.err().println("usage: while { condition } [do] { action }");
process.error(2);
return null;
}
while (isTrue(session, condition)) {
try {
action.execute(session, null);
} catch (BreakException b) {
break;
} catch (ContinueException c) {
// Ignore
}
}
return null;
}
protected Object doUntil(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"until - until loop",
"Usage: until { condition } [do] { action }",
" -? --help Show help",
};
Options opt = parseOptions(session, usage, argv);
Function condition = null;
Function action = null;
int step = 0;
boolean error = false;
for (Object obj : opt.argObjects()) {
if (condition == null) {
if (obj instanceof Function) {
condition = (Function) obj;
} else {
error = true;
break;
}
step = 1;
} else if ("do".equals(obj)) {
if (step != 1) {
error = true;
break;
}
step = 2;
} else if (step == 1 || step == 2) {
if (obj instanceof Function) {
action = (Function) obj;
} else {
error = true;
break;
}
step = 3;
} else {
error = true;
break;
}
}
error |= condition == null;
error |= action == null;
if (error) {
process.err().println("usage: until { condition } [do] { action }");
process.error(2);
return null;
}
while (!isTrue(session, condition)) {
try {
action.execute(session, null);
} catch (BreakException e) {
break;
} catch (ContinueException c) {
// Ignore
}
}
return null;
}
protected Object doBreak(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"break - break from loop",
"Usage: break",
" -? --help Show help",
};
parseOptions(session, usage, argv);
throw new BreakException();
}
protected Object doContinue(CommandSession session, Process process, Object[] argv) throws Exception {
String[] usage = {
"continue - continue loop",
"Usage: continue",
" -? --help Show help",
};
parseOptions(session, usage, argv);
throw new ContinueException();
}
private boolean isTrue(CommandSession session, Function function) throws Exception {
checkInterrupt();
return isTrue(function.execute(session, null));
}
private boolean isTrue(Object result) throws InterruptedException {
checkInterrupt();
if (result == null)
return false;
if (result instanceof Boolean)
return (Boolean) result;
if (result instanceof Number) {
if (0 == ((Number) result).intValue())
return false;
}
if ("".equals(result))
return false;
return !"0".equals(result);
}
private void checkInterrupt() throws InterruptedException {
if (Thread.currentThread().isInterrupted())
throw new InterruptedException("interrupted");
}
@SuppressWarnings("unchecked")
private Collection<Object> getElements(Options opt) {
Collection<Object> elements = null;
if (opt.argObjects().size() > 0) {
Object o = opt.argObjects().remove(0);
if (o instanceof Collection) {
elements = (Collection<Object>) o;
} else if (o != null && o.getClass().isArray()) {
elements = Arrays.asList((Object[]) o);
}
}
return elements;
}
private List<Function> getFunctions(Options opt) {
List<Function> functions = new ArrayList<>();
for (Object o : opt.argObjects()) {
if (o instanceof Function) {
functions.add((Function) o);
}
else {
functions = null;
break;
}
}
return functions;
}
}