001 /*
002 * Copyright (C) 2012 eXo Platform SAS.
003 *
004 * This is free software; you can redistribute it and/or modify it
005 * under the terms of the GNU Lesser General Public License as
006 * published by the Free Software Foundation; either version 2.1 of
007 * the License, or (at your option) any later version.
008 *
009 * This software 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 * You should have received a copy of the GNU Lesser General Public
015 * License along with this software; if not, write to the Free
016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
018 */
019
020 package org.crsh.processor.jline;
021
022 import jline.console.ConsoleReader;
023 import jline.console.completer.Completer;
024 import org.crsh.cmdline.CommandCompletion;
025 import org.crsh.cmdline.Delimiter;
026 import org.crsh.cmdline.spi.ValueCompletion;
027 import org.crsh.shell.Shell;
028 import org.crsh.shell.ShellProcess;
029 import org.crsh.shell.ShellResponse;
030
031 import java.io.IOException;
032 import java.io.PrintWriter;
033 import java.util.List;
034 import java.util.Map;
035 import java.util.concurrent.atomic.AtomicReference;
036
037 public class JLineProcessor implements Runnable, Completer {
038
039 /** . */
040 private final Shell shell;
041
042 /** . */
043 final ConsoleReader reader;
044
045 /** . */
046 final PrintWriter writer;
047
048 /** . */
049 final AtomicReference<ShellProcess> current;
050
051 public JLineProcessor(Shell shell, ConsoleReader reader, PrintWriter writer) {
052 this.shell = shell;
053 this.reader = reader;
054 this.writer = writer;
055 this.current = new AtomicReference<ShellProcess>();
056 }
057
058 public void run() {
059
060 //
061 String welcome = shell.getWelcome();
062 writer.println(welcome);
063 writer.flush();
064
065 //
066 while (true) {
067 String prompt = getPrompt();
068 String line;
069 try {
070 writer.println();
071 writer.flush();
072 if ((line = reader.readLine(prompt)) == null) {
073 break;
074 }
075 }
076 catch (IOException e) {
077 // What should we do other than that ?
078 break;
079 }
080
081 //
082 ShellProcess process = shell.createProcess(line);
083 JLineProcessContext context = new JLineProcessContext(this);
084 current.set(process);
085 try {
086 process.execute(context);
087 try {
088 context.latch.await();
089 }
090 catch (InterruptedException ignore) {
091 // At the moment
092 }
093 }
094 finally {
095 current.set(null);
096 }
097
098 //
099 ShellResponse response = context.resp.get();
100
101 // Write message
102 boolean flushed = false;
103 String msg = response.getMessage();
104 if (msg.length() > 0) {
105 writer.write(msg);
106 writer.flush();
107 flushed = true;
108 }
109
110 //
111 if (response instanceof ShellResponse.Cancelled) {
112 // Do nothing
113 } else if (response instanceof ShellResponse.Close) {
114 break;
115 } else {
116 if (!flushed) {
117 writer.flush();
118 }
119 }
120 }
121 }
122
123 public int complete(String buffer, int cursor, List<CharSequence> candidates) {
124 String prefix = buffer.substring(0, cursor);
125 CommandCompletion completion = shell.complete(prefix);
126 ValueCompletion vc = completion.getValue();
127 if (vc.isEmpty()) {
128 return -1;
129 }
130 Delimiter delimiter = completion.getDelimiter();
131 for (Map.Entry<String, Boolean> entry : vc) {
132 StringBuilder sb = new StringBuilder();
133 sb.append(vc.getPrefix());
134 try {
135 delimiter.escape(entry.getKey(), sb);
136 if (entry.getValue()) {
137 sb.append(completion.getDelimiter().getValue());
138 }
139 candidates.add(sb.toString());
140 }
141 catch (IOException ignore) {
142 }
143 }
144 return cursor - vc.getPrefix().length();
145 }
146
147 public void cancel() {
148 ShellProcess process = current.get();
149 if (process != null) {
150 process.cancel();
151 } else {
152 // Do nothing
153 }
154 }
155
156 String getPrompt() {
157 String prompt = shell.getPrompt();
158 return prompt == null ? "% " : prompt;
159 }
160 }