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    }