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.cli.impl.completion.CompletionMatch;
025    import org.crsh.cli.impl.Delimiter;
026    import org.crsh.cli.spi.Completion;
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      /** Whether or not we switched on the alternate screen. */
052      boolean useAlternate;
053    
054      public JLineProcessor(Shell shell, ConsoleReader reader, PrintWriter writer) {
055        this.shell = shell;
056        this.reader = reader;
057        this.writer = writer;
058        this.current = new AtomicReference<ShellProcess>();
059        this.useAlternate = false;
060      }
061    
062      public void run() {
063        String welcome = shell.getWelcome();
064        writer.println(welcome);
065        writer.flush();
066        loop();
067      }
068    
069      private void loop() {
070        while (true) {
071          String prompt = getPrompt();
072          String line;
073          try {
074            writer.println();
075            writer.flush();
076            if ((line = reader.readLine(prompt)) == null) {
077              break;
078            }
079          }
080          catch (IOException e) {
081            // What should we do other than that ?
082            break;
083          }
084    
085          //
086          ShellProcess process = shell.createProcess(line);
087          JLineProcessContext context = new JLineProcessContext(this);
088          current.set(process);
089          try {
090            process.execute(context);
091            try {
092              context.latch.await();
093            }
094            catch (InterruptedException ignore) {
095              // At the moment
096            }
097          }
098          finally {
099            current.set(null);
100          }
101    
102          //
103          ShellResponse response = context.resp.get();
104    
105          // Write message
106          boolean flushed = false;
107          String msg = response.getMessage();
108          if (msg.length() > 0) {
109            writer.write(msg);
110            writer.flush();
111            flushed = true;
112          }
113    
114          //
115          if (response instanceof ShellResponse.Cancelled) {
116            // Do nothing
117          } else if (response instanceof ShellResponse.Close) {
118            break;
119          } else {
120            if (!flushed) {
121              writer.flush();
122            }
123          }
124        }
125      }
126    
127      public int complete(String buffer, int cursor, List<CharSequence> candidates) {
128        String prefix = buffer.substring(0, cursor);
129        CompletionMatch completion = shell.complete(prefix);
130        Completion vc = completion.getValue();
131        if (vc.isEmpty()) {
132          return -1;
133        }
134        Delimiter delimiter = completion.getDelimiter();
135        for (Map.Entry<String, Boolean> entry : vc) {
136          StringBuilder sb = new StringBuilder();
137          sb.append(vc.getPrefix());
138          try {
139            delimiter.escape(entry.getKey(), sb);
140            if (entry.getValue()) {
141              sb.append(completion.getDelimiter().getValue());
142            }
143            candidates.add(sb.toString());
144          }
145          catch (IOException ignore) {
146          }
147        }
148        return cursor - vc.getPrefix().length();
149      }
150    
151      public void cancel() {
152        ShellProcess process = current.get();
153        if (process != null) {
154          process.cancel();
155        } else {
156          // Do nothing
157        }
158      }
159    
160      String getPrompt() {
161        String prompt = shell.getPrompt();
162        return prompt == null ? "% " : prompt;
163      }
164    }