/* * 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.Process; import org.apache.felix.service.command.CommandSession; import org.apache.felix.service.command.Function; 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; } protected static class OptionException extends Exception { public OptionException(String message, Throwable cause) { super(message, cause); } } protected static class HelpException extends Exception { public HelpException(String message) { super(message); } } protected static class ThrownException extends Exception { public ThrownException(Throwable cause) { super(cause); } } protected static class BreakException extends Exception { } 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 { 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); List<Function> functions = getFunctions(opt); if (elements == null || functions == null || functions.size() != 1) { process.err().println("usage: each elements { 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) { continue; } } 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} {if-action} ... {else-action}", " -? --help Show help", }; Options opt = parseOptions(session, usage, argv); List<Function> functions = getFunctions(opt); if (functions == null || functions.size() < 2) { process.err().println("usage: if {condition} {if-action} ... {else-action}"); process.error(2); return null; } for (int i = 0, length = functions.size(); i < length; ++i) { if (i == length - 1 || isTrue(session, ((Function) opt.argObjects().get(i++)))) { return ((Function) opt.argObjects().get(i)).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).initCause(cause)); } } protected Object doTry(CommandSession session, Process process, Object[] argv) throws Exception { String[] usage = { "try - try / catch / finally construct", "Usage: try { try-action } [ { catch-action } [ { finally-action } ] ]", " -? --help Show help", }; Options opt = parseOptions(session, usage, argv); List<Function> functions = getFunctions(opt); if (functions == null || functions.size() < 1 || functions.size() > 3) { process.err().println("usage: try { try-action } [ { catch-action } [ { finally-action } ] ]"); process.error(2); return null; } try { return functions.get(0).execute(session, null); } catch (BreakException b) { throw b; } catch (Exception e) { session.put("exception", e); if (functions.size() > 1) { functions.get(1).execute(session, null); } return null; } finally { if (functions.size() > 2) { functions.get(2).execute(session, null); } } } protected Object doWhile(CommandSession session, Process process, Object[] argv) throws Exception { String[] usage = { "while - while loop", "Usage: while { condition } { action }", " -? --help Show help", }; Options opt = parseOptions(session, usage, argv); List<Function> functions = getFunctions(opt); if (functions == null || functions.size() != 2) { process.err().println("usage: while { condition } { action }"); process.error(2); return null; } while (isTrue(session, functions.get(0))) { try { functions.get(1).execute(session, null); } catch (BreakException b) { break; } catch (ContinueException c) { continue; } } return null; } protected Object doUntil(CommandSession session, Process process, Object[] argv) throws Exception { String[] usage = { "until - until loop", "Usage: until { condition } { action }", " -? --help Show help", }; Options opt = parseOptions(session, usage, argv); List<Function> functions = new ArrayList<>(); for (Object o : opt.argObjects()) { if (o instanceof Function) { functions.add((Function) o); } else { functions = null; break; } } int length = opt.argObjects().size(); if (length != 2 || functions == null) { process.err().println("usage: until { condition } { action }"); process.error(2); return null; } while (!isTrue(session, functions.get(0))) { try { functions.get(1).execute(session, null); } catch (BreakException e) { break; } catch (ContinueException c) { continue; } } 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; if ("0".equals(result)) return false; return true; } 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; } }