001 /* 002 * Copyright (C) 2010 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.term; 021 022 import org.crsh.term.console.Console; 023 import org.crsh.term.console.ConsoleWriter; 024 import org.crsh.term.console.ViewWriter; 025 import org.crsh.term.spi.TermIO; 026 import org.crsh.text.CharReader; 027 import org.crsh.text.Style; 028 import org.slf4j.Logger; 029 import org.slf4j.LoggerFactory; 030 031 import java.io.IOException; 032 import java.util.LinkedList; 033 034 /** 035 * @author <a href="mailto:julien.viet@exoplatform.com">Julien Viet</a> 036 * @version $Revision$ 037 */ 038 public class BaseTerm implements Term { 039 040 /** . */ 041 private final Logger log = LoggerFactory.getLogger(BaseTerm.class); 042 043 /** . */ 044 private final LinkedList<CharSequence> history; 045 046 /** . */ 047 private CharSequence historyBuffer; 048 049 /** . */ 050 private int historyCursor; 051 052 /** . */ 053 private final TermIO io; 054 055 /** . */ 056 private final Console console; 057 058 public BaseTerm(final TermIO io) { 059 this.history = new LinkedList<CharSequence>(); 060 this.historyBuffer = null; 061 this.historyCursor = -1; 062 this.io = io; 063 this.console = new Console(new ViewWriter() { 064 065 @Override 066 protected void flush() throws IOException { 067 io.flush(); 068 } 069 070 @Override 071 protected void writeCRLF() throws IOException { 072 io.writeCRLF(); 073 } 074 075 @Override 076 protected void write(CharSequence s) throws IOException { 077 io.write(s.toString()); 078 } 079 080 @Override 081 protected void write(char c) throws IOException { 082 io.write(c); 083 } 084 085 @Override 086 protected void write(Style style) throws IOException { 087 io.write(style); 088 } 089 090 @Override 091 protected void writeDel() throws IOException { 092 io.writeDel(); 093 } 094 095 @Override 096 protected boolean writeMoveLeft() throws IOException { 097 return io.moveLeft(); 098 } 099 100 @Override 101 protected boolean writeMoveRight(char c) throws IOException { 102 return io.moveRight(c); 103 } 104 }); 105 } 106 107 public int getWidth() { 108 return io.getWidth(); 109 } 110 111 public String getProperty(String name) { 112 return io.getProperty(name); 113 } 114 115 public void setEcho(boolean echo) { 116 console.setEchoing(echo); 117 } 118 119 public TermEvent read() throws IOException { 120 121 // 122 while (true) { 123 int code = io.read(); 124 CodeType type = io.decode(code); 125 switch (type) { 126 case CLOSE: 127 return TermEvent.close(); 128 case BACKSPACE: 129 console.getViewReader().del(); 130 break; 131 case UP: 132 case DOWN: 133 int nextHistoryCursor = historyCursor + (type == CodeType.UP ? + 1 : -1); 134 if (nextHistoryCursor >= -1 && nextHistoryCursor < history.size()) { 135 CharSequence s = nextHistoryCursor == -1 ? historyBuffer : history.get(nextHistoryCursor); 136 while (console.getViewReader().moveRight()) { 137 // Do nothing 138 } 139 CharSequence t = console.getViewReader().replace(s); 140 if (historyCursor == -1) { 141 historyBuffer = t; 142 } 143 if (nextHistoryCursor == -1) { 144 historyBuffer = null; 145 } 146 historyCursor = nextHistoryCursor; 147 } 148 break; 149 case RIGHT: 150 console.getViewReader().moveRight(); 151 break; 152 case LEFT: 153 console.getViewReader().moveLeft(); 154 break; 155 case BREAK: 156 log.debug("Want to cancel evaluation"); 157 console.clearBuffer(); 158 return TermEvent.brk(); 159 case CHAR: 160 if (code >= 0 && code < 128) { 161 console.getViewReader().append((char)code); 162 } else { 163 log.debug("Unhandled char " + code); 164 } 165 break; 166 case TAB: 167 log.debug("Tab"); 168 return TermEvent.complete(console.getBufferToCursor()); 169 } 170 171 // 172 if (console.getReader().hasNext()) { 173 historyCursor = -1; 174 historyBuffer = null; 175 CharSequence input = console.getReader().next(); 176 return TermEvent.readLine(input); 177 } 178 } 179 } 180 181 public Appendable getInsertBuffer() { 182 return console.getViewReader(); 183 } 184 185 public void addToHistory(CharSequence line) { 186 history.addFirst(line); 187 } 188 189 public CharSequence getBuffer() { 190 return console.getBufferToCursor(); 191 } 192 193 public void close() { 194 try { 195 log.debug("Closing connection"); 196 io.flush(); 197 io.close(); 198 } catch (IOException e) { 199 log.debug("Exception thrown during term close()", e); 200 } 201 } 202 203 public void write(CharReader reader) throws IOException { 204 ConsoleWriter writer = console.getWriter(); 205 for (Object f : reader) { 206 if (f instanceof Style) { 207 writer.write((Style)f); 208 } else { 209 writer.write(f.toString()); 210 } 211 } 212 } 213 }