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 */ 019package org.crsh.console; 020 021import jline.console.Operation; 022import org.crsh.shell.Shell; 023import org.crsh.shell.ShellProcess; 024import org.crsh.util.Utils; 025 026import java.io.IOException; 027import java.util.concurrent.BlockingDeque; 028import java.util.concurrent.LinkedBlockingDeque; 029import java.util.concurrent.atomic.AtomicReference; 030 031/** 032 * A console state machine, which delegates the state machine to the {@link Plugin} implementation. 033 * 034 * @author Julien Viet 035 */ 036public class Console { 037 038 /** . */ 039 final Shell shell; 040 041 /** The current handler. */ 042 final AtomicReference<Plugin> handler; 043 044 /** The buffer. */ 045 final BlockingDeque<KeyStroke> buffer; 046 047 /** . */ 048 final ConsoleDriver driver; 049 050 /** . */ 051 final Editor editor; 052 053 /** . */ 054 boolean running; 055 056 public Console(Shell shell, ConsoleDriver driver) throws NullPointerException { 057 if (shell == null) { 058 throw new NullPointerException("No null shell accepted"); 059 } 060 this.driver = driver; 061 this.shell = shell; 062 this.buffer = new LinkedBlockingDeque<KeyStroke>(1024); 063 this.handler = new AtomicReference<Plugin>(); 064 this.editor = new Editor(this); 065 this.running = true; 066 } 067 068 public void setMode(Mode mode) { 069 editor.setMode(mode); 070 } 071 072 public void toEmacs() { 073 setMode(Mode.EMACS); 074 } 075 076 public void toMove() { 077 setMode(Mode.VI_MOVE); 078 } 079 080 public void toInsert() { 081 setMode(Mode.VI_INSERT); 082 } 083 084 public Mode getMode() { 085 return editor.getMode(); 086 } 087 088 public void addModeListener(Runnable runnable) { 089 editor.addModeListener(runnable); 090 } 091 092 public boolean isRunning() { 093 return running; 094 } 095 096 /** 097 * Initiali 098 */ 099 public void init() { 100 // Take care of pormpt 101 String welcome = shell.getWelcome(); 102 if (welcome != null && welcome.length() > 0) { 103 try { 104 driver.write(welcome); 105 driver.flush(); 106 } 107 catch (IOException e) { 108 // Log it 109 } 110 } 111 edit(); 112 } 113 114 public Iterable<KeyStroke> getKeyBuffer() { 115 return buffer; 116 } 117 118 public void on(Operation operation, int... buffer) { 119 on(new KeyStroke(operation, buffer)); 120 } 121 122 public void on(KeyStroke keyStroke) { 123 if (keyStroke.operation == Operation.INTERRUPT) { 124 Plugin current = handler.get(); 125 if (current == null) { 126 throw new IllegalStateException("Not initialized"); 127 } else if (current instanceof ProcessHandler) { 128 ProcessHandler processHandler = (ProcessHandler)current; 129 ProcessHandler.Reader reader = processHandler.editor.get(); 130 if (reader != null) { 131 reader.thread.interrupt(); 132 } 133 processHandler.process.cancel(); 134 return; 135 } 136 } 137 buffer.add(keyStroke); 138 iterate(); 139 } 140 141 public void on(KeyStroke[] keyStrokes) { 142 for (KeyStroke keyStroke : keyStrokes) { 143 on(keyStroke); 144 } 145 } 146 147 148 void close() { 149 running = false; 150 Utils.close(driver); 151 } 152 153 /** 154 * Switch to edit. 155 */ 156 Editor edit() { 157 String prompt = shell.getPrompt(); 158 if (prompt != null && prompt.length() > 0) { 159 try { 160 driver.write(prompt); 161 driver.flush(); 162 } 163 catch (IOException e) { 164 // Swallow for now... 165 } 166 } 167 editor.reset(); 168 handler.set(editor); 169 return editor; 170 } 171 172 /** 173 * Process the state machine. 174 */ 175 void iterate() { 176 while (running) { 177 Plugin current = handler.get(); 178 KeyStroke key = buffer.poll(); 179 if (key != null) { 180 if (current == null) { 181 throw new IllegalStateException("Not initialized"); 182 } else if (current instanceof Editor) { 183 Editor editor = (Editor)current; 184 EditorAction action = editor.getMode().on(key); 185 if (action != null) { 186 String line = editor.append(action, key.sequence); 187 if (line != null) { 188 ShellProcess process = shell.createProcess(line); 189 ProcessHandler context = new ProcessHandler(this, process); 190 handler.set(context); 191 process.execute(context); 192 } 193 } 194 } else if (current instanceof ProcessHandler) { 195 ProcessHandler processHandler = (ProcessHandler)current; 196 ProcessHandler.Reader reader = processHandler.editor.get(); 197 if (reader != null) { 198 EditorAction action = editor.getMode().on(key); 199 if (action != null) { 200 String s = reader.editor.append(action, key.sequence); 201 if (s != null) { 202 reader.line.add(s); 203 } 204 } 205 } else { 206 KeyHandler keyHandler = processHandler.process.getKeyHandler(); 207 if (keyHandler != null) { 208 KeyType type = KeyType.map(key.operation, key.sequence); 209 keyHandler.handle(type, key.sequence); 210 } else { 211 buffer.addFirst(key); 212 } 213 return; 214 } 215 } else { 216 throw new UnsupportedOperationException(); 217 } 218 } else { 219 return; 220 } 221 } 222 } 223}