001/*
002 * (C) Copyright 2006-2010 Nuxeo SAS (http://nuxeo.com/) and contributors.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the GNU Lesser General Public License
006 * (LGPL) version 2.1 which accompanies this distribution, and is available at
007 * http://www.gnu.org/licenses/lgpl.html
008 *
009 * This library is distributed in the hope that it will be useful,
010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012 * Lesser General Public License for more details.
013 *
014 * Contributors:
015 *     bstefanescu
016 */
017package org.nuxeo.shell.cmds;
018
019import java.io.IOException;
020import java.io.InputStream;
021import java.io.PrintWriter;
022import java.io.Writer;
023
024import jline.ANSIBuffer;
025import jline.ConsoleReader;
026import jline.Terminal;
027
028import org.nuxeo.shell.Command;
029import org.nuxeo.shell.Context;
030import org.nuxeo.shell.Shell;
031import org.nuxeo.shell.ShellConsole;
032import org.nuxeo.shell.ShellException;
033import org.nuxeo.shell.cmds.completors.ShellCompletor;
034
035/**
036 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
037 */
038@Command(name = "interactive", help = "Interactive shell")
039public class Interactive implements Runnable, ShellConsole {
040
041    protected static ConsoleReaderFactory factory;
042
043    public static void setConsoleReaderFactory(ConsoleReaderFactory factory) {
044        Interactive.factory = factory;
045    }
046
047    protected static String currentCmdLine;
048
049    @Context
050    protected Shell shell;
051
052    protected ConsoleReader console;
053
054    private static boolean isRunning = false;
055
056    private static InteractiveShellHandler handler;
057
058    public static void setHandler(InteractiveShellHandler exitHandler) {
059        Interactive.handler = exitHandler;
060    }
061
062    public static void reset() {
063        isRunning = false;
064    }
065
066    public Interactive() throws IOException {
067        console = factory != null ? factory.getConsoleReader() : new ConsoleReader();
068    }
069
070    /**
071     * Used in GUI mode
072     *
073     * @param shell
074     * @param in
075     * @param out
076     * @throws IOException
077     */
078    public Interactive(Shell shell, InputStream in, Writer out, Terminal term) throws IOException {
079        this.shell = shell;
080        console = new ConsoleReader(in, out, null, term);
081    }
082
083    public static String getCurrentCmdLine() {
084        return currentCmdLine;
085    }
086
087    public ConsoleReader getConsole() {
088        return console;
089    }
090
091    public Shell getShell() {
092        return shell;
093    }
094
095    public void run() {
096        if (handler != null) {
097            handler.enterInteractiveMode();
098        }
099        if (isRunning) { // avoid entering twice this command
100            return;
101        }
102        isRunning = true;
103        console.addCompletor(new ShellCompletor(this));
104        shell.setConsole(this);
105        console.setDefaultPrompt(getPrompt());
106        try {
107            loadHistory();
108            shell.hello();
109            try {
110                shell.getActiveRegistry().autorun(shell);
111            } catch (Throwable t) {
112                handleError(t);
113            }
114            while (true) {
115                try {
116                    String cmdline = console.readLine(getPrompt());
117                    currentCmdLine = cmdline;
118                    shell.run(cmdline);
119                } catch (Throwable t) {
120                    int r = handleError(t);
121                    if (r != 0) {
122                        isRunning = false;
123                        if (handler != null) {
124                            if (handler.exitInteractiveMode(r)) {
125                                return;
126                            }
127                        } else { // default exit mechanism
128                            shell.bye();
129                            System.exit(r < 0 ? 0 : r);
130                        }
131                    }
132                }
133            }
134        } catch (IOException e) {
135            e.printStackTrace();
136            System.exit(1);
137        } finally {
138            closeHistory();
139        }
140    }
141
142    /**
143     * Return non zero to stop the application. If a negative code is returned the application will stop normally
144     * otherwise it will stop using System.exit with the exit code as argument.
145     *
146     * @param t
147     * @return
148     * @throws IOException
149     */
150    protected int handleError(Throwable t) throws IOException {
151        if (t instanceof ShellException) {
152            ShellException e = (ShellException) t;
153            int r = e.getErrorCode();
154            if (r != 0) {
155                return r;
156            } else {
157                shell.setProperty("last.error", e);
158                console.printString(e.getMessage());
159                console.printNewline();
160                // console.printString(sw.toString());
161            }
162        } else {
163            ANSIBuffer buf = shell.newANSIBuffer();
164            buf.red(Trace.getStackTrace(t));
165            console.printString(buf.toString());
166        }
167        return 0;
168    }
169
170    public String getPrompt() {
171        return shell.getActiveRegistry().getPrompt(shell);
172    }
173
174    public void print(String msg) {
175        try {
176            console.printString(msg);
177        } catch (IOException e) {
178            throw new ShellException(e).setErrorCode(1);
179        }
180    }
181
182    public void println(String msg) {
183        try {
184            console.printString(msg);
185            console.printNewline();
186        } catch (IOException e) {
187            throw new ShellException(e).setErrorCode(1);
188        }
189    }
190
191    public void println() {
192        try {
193            console.printNewline();
194        } catch (IOException e) {
195            throw new ShellException(e).setErrorCode(1);
196        }
197    }
198
199    public String readLine(String prompt, Character mask) {
200        try {
201            return console.readLine(prompt, mask);
202        } catch (IOException e) {
203            throw new ShellException(e).setErrorCode(1);
204        }
205    }
206
207    public void loadHistory() throws IOException {
208        if (!shell.getBooleanSetting("history", true)) {
209            return;
210        }
211        console.getHistory().setHistoryFile(shell.getHistoryFile());
212        console.getHistory().moveToEnd();
213    }
214
215    public void closeHistory() {
216        PrintWriter pw = console.getHistory().getOutput();
217        if (pw != null) {
218            pw.close();
219        }
220    }
221
222    public void removeHistory() {
223        closeHistory();
224        shell.getHistoryFile().delete();
225    }
226
227}