| 1 | /* |
| 2 | * This file is part of lanterna (http://code.google.com/p/lanterna/). |
| 3 | * |
| 4 | * lanterna is free software: you can redistribute it and/or modify |
| 5 | * it under the terms of the GNU Lesser General Public License as published by |
| 6 | * the Free Software Foundation, either version 3 of the License, or |
| 7 | * (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU Lesser General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU Lesser General Public License |
| 15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 16 | * |
| 17 | * Copyright (C) 2010-2015 Martin |
| 18 | */ |
| 19 | package com.googlecode.lanterna.terminal; |
| 20 | |
| 21 | import com.googlecode.lanterna.SGR; |
| 22 | import com.googlecode.lanterna.TerminalSize; |
| 23 | import com.googlecode.lanterna.TextColor; |
| 24 | import com.googlecode.lanterna.graphics.TextGraphics; |
| 25 | import com.googlecode.lanterna.input.KeyStroke; |
| 26 | import java.io.IOException; |
| 27 | import java.util.concurrent.TimeUnit; |
| 28 | |
| 29 | /** |
| 30 | * This class exposes methods for converting a terminal into an IOSafeTerminal. There are two options available, either |
| 31 | * one that will convert any IOException to a RuntimeException (and re-throw it) or one that will silently swallow any |
| 32 | * IOException (and return null in those cases the method has a non-void return type). |
| 33 | * @author Martin |
| 34 | */ |
| 35 | public class IOSafeTerminalAdapter implements IOSafeTerminal { |
| 36 | private interface ExceptionHandler { |
| 37 | void onException(IOException e); |
| 38 | } |
| 39 | |
| 40 | private static class ConvertToRuntimeException implements ExceptionHandler { |
| 41 | @Override |
| 42 | public void onException(IOException e) { |
| 43 | throw new RuntimeException(e); |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | private static class DoNothingAndOrReturnNull implements ExceptionHandler { |
| 48 | @Override |
| 49 | public void onException(IOException e) { } |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Creates a wrapper around a Terminal that exposes it as a IOSafeTerminal. If any IOExceptions occur, they will be |
| 54 | * wrapped by a RuntimeException and re-thrown. |
| 55 | * @param terminal Terminal to wrap |
| 56 | * @return IOSafeTerminal wrapping the supplied terminal |
| 57 | */ |
| 58 | public static IOSafeTerminal createRuntimeExceptionConvertingAdapter(Terminal terminal) { |
| 59 | if (terminal instanceof ExtendedTerminal) { // also handle Runtime-type: |
| 60 | return createRuntimeExceptionConvertingAdapter((ExtendedTerminal)terminal); |
| 61 | } else { |
| 62 | return new IOSafeTerminalAdapter(terminal, new ConvertToRuntimeException()); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | /** |
| 67 | * Creates a wrapper around an ExtendedTerminal that exposes it as a IOSafeExtendedTerminal. |
| 68 | * If any IOExceptions occur, they will be wrapped by a RuntimeException and re-thrown. |
| 69 | * @param terminal Terminal to wrap |
| 70 | * @return IOSafeTerminal wrapping the supplied terminal |
| 71 | */ |
| 72 | public static IOSafeExtendedTerminal createRuntimeExceptionConvertingAdapter(ExtendedTerminal terminal) { |
| 73 | return new IOSafeTerminalAdapter.Extended(terminal, new ConvertToRuntimeException()); |
| 74 | } |
| 75 | |
| 76 | /** |
| 77 | * Creates a wrapper around a Terminal that exposes it as a IOSafeTerminal. If any IOExceptions occur, they will be |
| 78 | * silently ignored and for those method with a non-void return type, null will be returned. |
| 79 | * @param terminal Terminal to wrap |
| 80 | * @return IOSafeTerminal wrapping the supplied terminal |
| 81 | */ |
| 82 | public static IOSafeTerminal createDoNothingOnExceptionAdapter(Terminal terminal) { |
| 83 | if (terminal instanceof ExtendedTerminal) { // also handle Runtime-type: |
| 84 | return createDoNothingOnExceptionAdapter((ExtendedTerminal)terminal); |
| 85 | } else { |
| 86 | return new IOSafeTerminalAdapter(terminal, new DoNothingAndOrReturnNull()); |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | /** |
| 91 | * Creates a wrapper around an ExtendedTerminal that exposes it as a IOSafeExtendedTerminal. |
| 92 | * If any IOExceptions occur, they will be silently ignored and for those method with a |
| 93 | * non-void return type, null will be returned. |
| 94 | * @param terminal Terminal to wrap |
| 95 | * @return IOSafeTerminal wrapping the supplied terminal |
| 96 | */ |
| 97 | public static IOSafeExtendedTerminal createDoNothingOnExceptionAdapter(ExtendedTerminal terminal) { |
| 98 | return new IOSafeTerminalAdapter.Extended(terminal, new DoNothingAndOrReturnNull()); |
| 99 | } |
| 100 | |
| 101 | private final Terminal backend; |
| 102 | final ExceptionHandler exceptionHandler; |
| 103 | |
| 104 | @SuppressWarnings("WeakerAccess") |
| 105 | public IOSafeTerminalAdapter(Terminal backend, ExceptionHandler exceptionHandler) { |
| 106 | this.backend = backend; |
| 107 | this.exceptionHandler = exceptionHandler; |
| 108 | } |
| 109 | |
| 110 | @Override |
| 111 | public void enterPrivateMode() { |
| 112 | try { |
| 113 | backend.enterPrivateMode(); |
| 114 | } |
| 115 | catch(IOException e) { |
| 116 | exceptionHandler.onException(e); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | @Override |
| 121 | public void exitPrivateMode() { |
| 122 | try { |
| 123 | backend.exitPrivateMode(); |
| 124 | } |
| 125 | catch(IOException e) { |
| 126 | exceptionHandler.onException(e); |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | @Override |
| 131 | public void clearScreen() { |
| 132 | try { |
| 133 | backend.clearScreen(); |
| 134 | } |
| 135 | catch(IOException e) { |
| 136 | exceptionHandler.onException(e); |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | @Override |
| 141 | public void setCursorPosition(int x, int y) { |
| 142 | try { |
| 143 | backend.setCursorPosition(x, y); |
| 144 | } |
| 145 | catch(IOException e) { |
| 146 | exceptionHandler.onException(e); |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | @Override |
| 151 | public void setCursorVisible(boolean visible) { |
| 152 | try { |
| 153 | backend.setCursorVisible(visible); |
| 154 | } |
| 155 | catch(IOException e) { |
| 156 | exceptionHandler.onException(e); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | @Override |
| 161 | public void putCharacter(char c) { |
| 162 | try { |
| 163 | backend.putCharacter(c); |
| 164 | } |
| 165 | catch(IOException e) { |
| 166 | exceptionHandler.onException(e); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | @Override |
| 171 | public TextGraphics newTextGraphics() throws IOException { |
| 172 | return backend.newTextGraphics(); |
| 173 | } |
| 174 | |
| 175 | @Override |
| 176 | public void enableSGR(SGR sgr) { |
| 177 | try { |
| 178 | backend.enableSGR(sgr); |
| 179 | } |
| 180 | catch(IOException e) { |
| 181 | exceptionHandler.onException(e); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | @Override |
| 186 | public void disableSGR(SGR sgr) { |
| 187 | try { |
| 188 | backend.disableSGR(sgr); |
| 189 | } |
| 190 | catch(IOException e) { |
| 191 | exceptionHandler.onException(e); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | @Override |
| 196 | public void resetColorAndSGR() { |
| 197 | try { |
| 198 | backend.resetColorAndSGR(); |
| 199 | } |
| 200 | catch(IOException e) { |
| 201 | exceptionHandler.onException(e); |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | @Override |
| 206 | public void setForegroundColor(TextColor color) { |
| 207 | try { |
| 208 | backend.setForegroundColor(color); |
| 209 | } |
| 210 | catch(IOException e) { |
| 211 | exceptionHandler.onException(e); |
| 212 | } |
| 213 | } |
| 214 | |
| 215 | @Override |
| 216 | public void setBackgroundColor(TextColor color) { |
| 217 | try { |
| 218 | backend.setBackgroundColor(color); |
| 219 | } |
| 220 | catch(IOException e) { |
| 221 | exceptionHandler.onException(e); |
| 222 | } |
| 223 | } |
| 224 | |
| 225 | @Override |
| 226 | public void addResizeListener(ResizeListener listener) { |
| 227 | backend.addResizeListener(listener); |
| 228 | } |
| 229 | |
| 230 | @Override |
| 231 | public void removeResizeListener(ResizeListener listener) { |
| 232 | backend.removeResizeListener(listener); |
| 233 | } |
| 234 | |
| 235 | @Override |
| 236 | public TerminalSize getTerminalSize() { |
| 237 | try { |
| 238 | return backend.getTerminalSize(); |
| 239 | } |
| 240 | catch(IOException e) { |
| 241 | exceptionHandler.onException(e); |
| 242 | } |
| 243 | return null; |
| 244 | } |
| 245 | |
| 246 | @Override |
| 247 | public byte[] enquireTerminal(int timeout, TimeUnit timeoutUnit) { |
| 248 | try { |
| 249 | return backend.enquireTerminal(timeout, timeoutUnit); |
| 250 | } |
| 251 | catch(IOException e) { |
| 252 | exceptionHandler.onException(e); |
| 253 | } |
| 254 | return null; |
| 255 | } |
| 256 | |
| 257 | @Override |
| 258 | public void flush() { |
| 259 | try { |
| 260 | backend.flush(); |
| 261 | } |
| 262 | catch(IOException e) { |
| 263 | exceptionHandler.onException(e); |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | @Override |
| 268 | public KeyStroke pollInput() { |
| 269 | try { |
| 270 | return backend.pollInput(); |
| 271 | } |
| 272 | catch(IOException e) { |
| 273 | exceptionHandler.onException(e); |
| 274 | } |
| 275 | return null; |
| 276 | } |
| 277 | |
| 278 | @Override |
| 279 | public KeyStroke readInput() { |
| 280 | try { |
| 281 | return backend.readInput(); |
| 282 | } |
| 283 | catch(IOException e) { |
| 284 | exceptionHandler.onException(e); |
| 285 | } |
| 286 | return null; |
| 287 | } |
| 288 | |
| 289 | /** |
| 290 | * This class exposes methods for converting an extended terminal into an IOSafeExtendedTerminal. |
| 291 | */ |
| 292 | public static class Extended extends IOSafeTerminalAdapter implements IOSafeExtendedTerminal { |
| 293 | private final ExtendedTerminal backend; |
| 294 | |
| 295 | public Extended(ExtendedTerminal backend, ExceptionHandler exceptionHandler) { |
| 296 | super(backend, exceptionHandler); |
| 297 | this.backend = backend; |
| 298 | } |
| 299 | |
| 300 | @Override |
| 301 | public void setTerminalSize(int columns, int rows) { |
| 302 | try { |
| 303 | backend.setTerminalSize(columns, rows); |
| 304 | } |
| 305 | catch(IOException e) { |
| 306 | exceptionHandler.onException(e); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | @Override |
| 311 | public void setTitle(String title) { |
| 312 | try { |
| 313 | backend.setTitle(title); |
| 314 | } |
| 315 | catch(IOException e) { |
| 316 | exceptionHandler.onException(e); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | @Override |
| 321 | public void pushTitle() { |
| 322 | try { |
| 323 | backend.pushTitle(); |
| 324 | } |
| 325 | catch(IOException e) { |
| 326 | exceptionHandler.onException(e); |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | @Override |
| 331 | public void popTitle() { |
| 332 | try { |
| 333 | backend.popTitle(); |
| 334 | } |
| 335 | catch(IOException e) { |
| 336 | exceptionHandler.onException(e); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | @Override |
| 341 | public void iconify() { |
| 342 | try { |
| 343 | backend.iconify(); |
| 344 | } |
| 345 | catch(IOException e) { |
| 346 | exceptionHandler.onException(e); |
| 347 | } |
| 348 | } |
| 349 | |
| 350 | @Override |
| 351 | public void deiconify() { |
| 352 | try { |
| 353 | backend.deiconify(); |
| 354 | } |
| 355 | catch(IOException e) { |
| 356 | exceptionHandler.onException(e); |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | @Override |
| 361 | public void maximize() { |
| 362 | try { |
| 363 | backend.maximize(); |
| 364 | } |
| 365 | catch(IOException e) { |
| 366 | exceptionHandler.onException(e); |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | @Override |
| 371 | public void unmaximize() { |
| 372 | try { |
| 373 | backend.unmaximize(); |
| 374 | } |
| 375 | catch(IOException e) { |
| 376 | exceptionHandler.onException(e); |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | @Override |
| 381 | public void setMouseCaptureMode(MouseCaptureMode mouseCaptureMode) { |
| 382 | try { |
| 383 | backend.setMouseCaptureMode(mouseCaptureMode); |
| 384 | } |
| 385 | catch(IOException e) { |
| 386 | exceptionHandler.onException(e); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | @Override |
| 391 | public void scrollLines(int firstLine, int lastLine, int distance) { |
| 392 | try { |
| 393 | backend.scrollLines(firstLine, lastLine, distance); |
| 394 | } |
| 395 | catch(IOException e) { |
| 396 | exceptionHandler.onException(e); |
| 397 | } |
| 398 | } |
| 399 | |
| 400 | } |
| 401 | } |