src/main/java/org/vaadin/console/Console.java
author indvdum
Tue, 31 May 2011 15:52:03 +0400
branchconsoleWithANSI
changeset 15 ea825ba6336b
parent 14 f6b22b2ab1b9
permissions -rw-r--r--
+ console 1.2.0-SNAPSHOT
indvdum@0
     1
package org.vaadin.console;
indvdum@0
     2
indvdum@0
     3
import java.io.ByteArrayOutputStream;
indvdum@0
     4
import java.io.IOException;
indvdum@0
     5
import java.io.OutputStream;
indvdum@0
     6
import java.io.PrintStream;
indvdum@0
     7
import java.io.Serializable;
indvdum@0
     8
import java.util.ArrayList;
indvdum@0
     9
import java.util.Collections;
indvdum@0
    10
import java.util.HashMap;
indvdum@0
    11
import java.util.HashSet;
indvdum@0
    12
import java.util.List;
indvdum@0
    13
import java.util.Map;
indvdum@0
    14
import java.util.Set;
indvdum@15
    15
import java.util.regex.Pattern;
indvdum@0
    16
indvdum@15
    17
import org.vaadin.console.ansi.ANSICodeConverter;
indvdum@15
    18
import org.vaadin.console.ansi.DefaultANSICodeConverter;
indvdum@0
    19
import org.vaadin.rpc.ServerSideHandler;
indvdum@0
    20
import org.vaadin.rpc.ServerSideProxy;
indvdum@0
    21
import org.vaadin.rpc.client.Method;
indvdum@0
    22
indvdum@0
    23
import com.vaadin.terminal.PaintException;
indvdum@0
    24
import com.vaadin.terminal.PaintTarget;
indvdum@0
    25
import com.vaadin.ui.AbstractComponent;
indvdum@0
    26
import com.vaadin.ui.Component;
indvdum@0
    27
indvdum@0
    28
/**
indvdum@0
    29
 * Server side component for the VTextConsole widget.
indvdum@0
    30
 *
indvdum@0
    31
 * @author Sami Ekblad / Vaadin
indvdum@0
    32
 *
indvdum@0
    33
 */
indvdum@0
    34
@com.vaadin.ui.ClientWidget(org.vaadin.console.client.ui.VTextConsole.class)
indvdum@0
    35
public class Console extends AbstractComponent implements Component.Focusable {
indvdum@0
    36
indvdum@0
    37
    private static final long serialVersionUID = 590258219352859644L;
indvdum@0
    38
    private Handler handler;
indvdum@15
    39
    private ANSICodeConverter ansiToCSSconverter;
indvdum@15
    40
    private boolean isConvertANSIToCSS = false;
indvdum@0
    41
    private final HashMap<String, Command> commands = new HashMap<String, Command>();
indvdum@0
    42
    private final Config config = new Config();
indvdum@0
    43
indvdum@0
    44
    private static final String DEFAULT_PS = "}> ";
indvdum@0
    45
    private static final String DEFAULT_GREETING = "Console ready.";
indvdum@0
    46
    private static final int DEFAULT_BUFFER = 0;
indvdum@0
    47
    private static final int DEFAULT_COLS = -1;
indvdum@0
    48
    private static final int DEFAULT_ROWS = -1;
indvdum@0
    49
    private static final boolean DEFAULT_WRAP = true;
indvdum@9
    50
    private static final boolean DEFAULT_PRINT_PROMPT_ON_INPUT = true;
indvdum@9
    51
    private static final boolean DEFAULT_SMART_SCROLL_TO_END = false;
indvdum@0
    52
    private static final int MAX_COLS = 500;
indvdum@0
    53
    private static final int MAX_ROWS = 200;
indvdum@0
    54
indvdum@0
    55
    public boolean isWrap() {
indvdum@0
    56
        return config.wrap;
indvdum@0
    57
    }
indvdum@0
    58
indvdum@0
    59
    public void setWrap(final boolean wrap) {
indvdum@0
    60
        config.wrap = wrap;
indvdum@0
    61
        client.call("setWrap", wrap);
indvdum@0
    62
    }
indvdum@9
    63
indvdum@10
    64
	/**
indvdum@10
    65
	 * @return true, if entered by user text immediately will print to console, false otherwise
indvdum@10
    66
	 */
indvdum@9
    67
	public boolean isPrintPromptOnInput() {
indvdum@9
    68
		return config.isPrintPromptOnInput;
indvdum@9
    69
	}
indvdum@9
    70
indvdum@11
    71
	/**
indvdum@11
    72
	 * @param isPrintPromptOnInput if true - entered by user text immediately will be printed to 
indvdum@11
    73
	 * console, nothing happens otherwise
indvdum@11
    74
	 */
indvdum@9
    75
	public void setPrintPromptOnInput(final boolean isPrintPromptOnInput) {
indvdum@9
    76
		config.isPrintPromptOnInput = isPrintPromptOnInput;
indvdum@9
    77
		client.call("setPrintPromptOnInput", isPrintPromptOnInput);
indvdum@9
    78
	}
indvdum@9
    79
indvdum@10
    80
	/**
indvdum@10
    81
	 * @return true, if method scrollToEnd will only work if last scroll state was "end" 
indvdum@10
    82
	 * (like Linux KDE console emulator "Konsole"), false otherwise (by default)
indvdum@10
    83
	 */
indvdum@9
    84
	public boolean isSmartScrollToEnd() {
indvdum@9
    85
		return config.isSmartScrollToEnd;
indvdum@9
    86
	}
indvdum@9
    87
indvdum@10
    88
	/**
indvdum@10
    89
	 * @param isSmartScrollToEnd if true - method scrollToEnd will only work if last scroll 
indvdum@10
    90
	 * state was "end" (like Linux KDE console emulator "Konsole"); false by default
indvdum@10
    91
	 */
indvdum@9
    92
	public void setSmartScrollToEnd(final boolean isSmartScrollToEnd) {
indvdum@9
    93
		config.isSmartScrollToEnd = isSmartScrollToEnd;
indvdum@9
    94
		client.call("setSmartScrollToEnd", isSmartScrollToEnd);
indvdum@9
    95
	}
indvdum@0
    96
indvdum@0
    97
    private final ServerSideProxy client = new ServerSideProxy(
indvdum@0
    98
            new ClientCallback());
indvdum@0
    99
indvdum@0
   100
    /**
indvdum@0
   101
     * Inner class to handle client calls.
indvdum@0
   102
     *
indvdum@0
   103
     */
indvdum@0
   104
    public class ClientCallback implements ServerSideHandler {
indvdum@0
   105
indvdum@0
   106
        private static final long serialVersionUID = 3992611573500588703L;
indvdum@0
   107
indvdum@0
   108
        public void requestRepaint() {
indvdum@0
   109
            Console.this.requestRepaint();
indvdum@0
   110
        }
indvdum@0
   111
indvdum@0
   112
        public Object[] initRequestFromClient() {
indvdum@9
   113
            return new Object[] { 
indvdum@9
   114
            		config.cols, 
indvdum@9
   115
            		config.rows,
indvdum@9
   116
                    config.maxBufferSize, 
indvdum@9
   117
                    config.wrap, 
indvdum@9
   118
                    config.isPrintPromptOnInput, 
indvdum@9
   119
                    config.isSmartScrollToEnd, 
indvdum@9
   120
                    config.greeting, 
indvdum@9
   121
                    config.ps 
indvdum@9
   122
                    };
indvdum@0
   123
        }
indvdum@0
   124
indvdum@0
   125
        public void callFromClient(String method, Object[] params) {
indvdum@0
   126
            // TODO Auto-generated method stub
indvdum@0
   127
indvdum@0
   128
        }
indvdum@0
   129
indvdum@0
   130
    }
indvdum@0
   131
indvdum@0
   132
    /**
indvdum@0
   133
     * The tab order number of this field.
indvdum@0
   134
     */
indvdum@0
   135
    private int tabIndex = 0;
indvdum@0
   136
    @SuppressWarnings("unused")
indvdum@0
   137
    private Integer fontw;
indvdum@0
   138
    @SuppressWarnings("unused")
indvdum@0
   139
    private Integer fonth;
indvdum@0
   140
    private PrintStream printStream;
indvdum@0
   141
    private String lastSuggestInput;
indvdum@0
   142
    private List<CommandProvider> commandProviders;
indvdum@0
   143
indvdum@0
   144
    /**
indvdum@0
   145
     * An inner class for holding the configuration data.
indvdum@0
   146
     */
indvdum@0
   147
    public static class Config implements Serializable {
indvdum@0
   148
indvdum@0
   149
        private static final long serialVersionUID = -812601232248504108L;
indvdum@0
   150
indvdum@0
   151
        int maxBufferSize = DEFAULT_BUFFER;
indvdum@0
   152
        int cols = DEFAULT_COLS;
indvdum@0
   153
        int rows = DEFAULT_ROWS;
indvdum@0
   154
        boolean wrap = DEFAULT_WRAP;
indvdum@4
   155
        boolean isPrintPromptOnInput = DEFAULT_PRINT_PROMPT_ON_INPUT;
indvdum@9
   156
        boolean isSmartScrollToEnd = DEFAULT_SMART_SCROLL_TO_END;
indvdum@0
   157
        String ps = DEFAULT_PS;
indvdum@0
   158
        String greeting = DEFAULT_GREETING;
indvdum@0
   159
indvdum@0
   160
    }
indvdum@0
   161
indvdum@0
   162
    /**
indvdum@0
   163
     * Console Handler interface.
indvdum@0
   164
     *
indvdum@0
   165
     * Handler provides a hook to handle various console related events and
indvdum@0
   166
     * override the default processing.
indvdum@0
   167
     *
indvdum@0
   168
     */
indvdum@0
   169
    public interface Handler extends Serializable {
indvdum@0
   170
indvdum@0
   171
        /**
indvdum@0
   172
         * Called when user uses TAB to complete the command entered into the
indvdum@0
   173
         * Console input.
indvdum@0
   174
         *
indvdum@0
   175
         * @param console
indvdum@0
   176
         * @param lastInput
indvdum@0
   177
         * @return
indvdum@0
   178
         */
indvdum@0
   179
        Set<String> getSuggestions(Console console, String lastInput);
indvdum@0
   180
indvdum@0
   181
        /**
indvdum@0
   182
         * Called when user has entered input to the Console and presses enter
indvdum@0
   183
         * to execute it.
indvdum@0
   184
         *
indvdum@0
   185
         * @param console
indvdum@0
   186
         * @param lastInput
indvdum@0
   187
         */
indvdum@0
   188
        void inputReceived(Console console, String lastInput);
indvdum@0
   189
indvdum@0
   190
        /**
indvdum@0
   191
         * Handle an exception during a Command execution.
indvdum@0
   192
         *
indvdum@0
   193
         * @param console
indvdum@0
   194
         * @param e
indvdum@0
   195
         * @param cmd
indvdum@0
   196
         * @param argv
indvdum@0
   197
         */
indvdum@0
   198
        void handleException(Console console, Exception e, Command cmd,
indvdum@0
   199
                String[] argv);
indvdum@0
   200
indvdum@0
   201
        /**
indvdum@0
   202
         * Handle situation where a command could not be found.
indvdum@0
   203
         *
indvdum@0
   204
         * @param console
indvdum@0
   205
         * @param argv
indvdum@0
   206
         */
indvdum@0
   207
        void commandNotFound(Console console, String[] argv);
indvdum@0
   208
indvdum@0
   209
    }
indvdum@0
   210
indvdum@0
   211
    /**
indvdum@0
   212
     * Commands that can be executed against the Component instance. They
indvdum@0
   213
     * provide convenient string to method mapping. Basically, a Command is a
indvdum@0
   214
     * method that that can be executed in Component. It can have parameters or
indvdum@0
   215
     * not.
indvdum@0
   216
     *
indvdum@0
   217
     */
indvdum@0
   218
    public interface Command extends Serializable {
indvdum@0
   219
indvdum@0
   220
        /**
indvdum@0
   221
         * Execute a Command with arguments.
indvdum@0
   222
         *
indvdum@0
   223
         * @param console
indvdum@0
   224
         * @param argv
indvdum@0
   225
         * @return
indvdum@0
   226
         * @throws Exception
indvdum@0
   227
         */
indvdum@0
   228
        public Object execute(Console console, String[] argv) throws Exception;
indvdum@0
   229
indvdum@0
   230
        /**
indvdum@0
   231
         * Get usage information about this command.
indvdum@0
   232
         *
indvdum@0
   233
         * @param console
indvdum@0
   234
         * @param argv
indvdum@0
   235
         * @return
indvdum@0
   236
         */
indvdum@0
   237
        public String getUsage(Console console, String[] argv);
indvdum@0
   238
    }
indvdum@0
   239
indvdum@0
   240
    /**
indvdum@0
   241
     * Interface for providing Commands to the console. One can register a
indvdum@0
   242
     * command providers to console instead of individual commands to provide a
indvdum@0
   243
     * lot of commands.
indvdum@0
   244
     *
indvdum@0
   245
     */
indvdum@0
   246
    public interface CommandProvider extends Serializable {
indvdum@0
   247
indvdum@0
   248
        /**
indvdum@0
   249
         * List all available command from this provider.
indvdum@0
   250
         *
indvdum@0
   251
         * @param console
indvdum@0
   252
         * @return
indvdum@0
   253
         */
indvdum@0
   254
        Set<String> getAvailableCommands(Console console);
indvdum@0
   255
indvdum@0
   256
        /**
indvdum@0
   257
         * Get Command instance based on command name.
indvdum@0
   258
         *
indvdum@0
   259
         * @param console
indvdum@0
   260
         * @param commandName
indvdum@0
   261
         * @return
indvdum@0
   262
         */
indvdum@0
   263
        Command getCommand(Console console, String commandName);
indvdum@0
   264
indvdum@0
   265
    }
indvdum@0
   266
indvdum@0
   267
    public void addCommandProvider(final CommandProvider commandProvider) {
indvdum@0
   268
        if (commandProviders == null) {
indvdum@0
   269
            commandProviders = new ArrayList<CommandProvider>();
indvdum@0
   270
        }
indvdum@0
   271
        commandProviders.add(commandProvider);
indvdum@0
   272
    }
indvdum@0
   273
indvdum@0
   274
    public void removeCommandProvider(final CommandProvider commandProvider) {
indvdum@0
   275
        if (commandProviders == null) {
indvdum@0
   276
            return;
indvdum@0
   277
        }
indvdum@0
   278
        commandProviders.remove(commandProvider);
indvdum@0
   279
    }
indvdum@0
   280
indvdum@0
   281
    public void removeAllCommandProviders() {
indvdum@0
   282
        if (commandProviders == null) {
indvdum@0
   283
            return;
indvdum@0
   284
        }
indvdum@0
   285
        commandProviders.clear();
indvdum@0
   286
    }
indvdum@0
   287
indvdum@0
   288
    public Console(final Console.Handler handler) {
indvdum@0
   289
        this();
indvdum@0
   290
        setHandler(handler);
indvdum@0
   291
    }
indvdum@0
   292
indvdum@0
   293
    public Console() {
indvdum@0
   294
        setImmediate(true);
indvdum@0
   295
        setHandler(new DefaultConsoleHandler());
indvdum@15
   296
        setANSIToCSSConverter(new DefaultANSICodeConverter());
indvdum@0
   297
        registerCallbacks();
indvdum@0
   298
    }
indvdum@0
   299
indvdum@0
   300
    private void registerCallbacks() {
indvdum@0
   301
        client.register("suggest", new Method() {
indvdum@0
   302
indvdum@0
   303
            public void invoke(String methodName, Object[] params) {
indvdum@0
   304
                handleSuggest((String) params[0]);
indvdum@0
   305
            }
indvdum@0
   306
        });
indvdum@0
   307
        client.register("input", new Method() {
indvdum@0
   308
indvdum@0
   309
            public void invoke(String methodName, Object[] params) {
indvdum@0
   310
                handleInput((String) params[0]);
indvdum@0
   311
            }
indvdum@0
   312
        });
indvdum@0
   313
indvdum@0
   314
    }
indvdum@0
   315
indvdum@0
   316
    @Override
indvdum@0
   317
    public void paintContent(final PaintTarget target) throws PaintException {
indvdum@0
   318
        super.paintContent(target);
indvdum@0
   319
        client.paintContent(target);
indvdum@0
   320
    }
indvdum@0
   321
indvdum@0
   322
    @Override
indvdum@0
   323
    public void changeVariables(final Object source,
indvdum@0
   324
            final Map<String, Object> variables) {
indvdum@0
   325
        super.changeVariables(source, variables);
indvdum@0
   326
        client.changeVariables(source, variables);
indvdum@0
   327
        if (variables.containsKey("width")) {
indvdum@0
   328
            setWidth((String) variables.get("width"));
indvdum@0
   329
        }
indvdum@0
   330
        if (variables.containsKey("height")) {
indvdum@0
   331
            setHeight((String) variables.get("height"));
indvdum@0
   332
        }
indvdum@0
   333
        if (variables.containsKey("fontw")) {
indvdum@0
   334
            fontw = (Integer) variables.get("fontw");
indvdum@0
   335
        }
indvdum@0
   336
        if (variables.containsKey("fonth")) {
indvdum@0
   337
            fonth = (Integer) variables.get("fonth");
indvdum@0
   338
        }
indvdum@0
   339
        if (variables.containsKey("cols")) {
indvdum@0
   340
            config.cols = (Integer) variables.get("cols");
indvdum@0
   341
        }
indvdum@0
   342
        if (variables.containsKey("rows")) {
indvdum@0
   343
            config.rows = (Integer) variables.get("rows");
indvdum@0
   344
        }
indvdum@0
   345
    }
indvdum@0
   346
indvdum@0
   347
    /**
indvdum@0
   348
     * Overridden to filter client-side calculation/changes and avoid loops.
indvdum@0
   349
     *
indvdum@0
   350
     */
indvdum@0
   351
    @Override
indvdum@0
   352
    public void setWidth(float width, int unit) {
indvdum@0
   353
        if (width != getWidth() || unit != getWidthUnits()) {
indvdum@0
   354
            super.setWidth(width, unit);
indvdum@0
   355
        }
indvdum@0
   356
    }
indvdum@0
   357
indvdum@0
   358
    /**
indvdum@0
   359
     * Overridden to filter client-side calculation/changes and avoid loops.
indvdum@0
   360
     *
indvdum@0
   361
     */
indvdum@0
   362
    @Override
indvdum@0
   363
    public void setHeight(float height, int unit) {
indvdum@0
   364
        if (height != getHeight() || unit != getHeightUnits()) {
indvdum@0
   365
            super.setHeight(height, unit);
indvdum@0
   366
        }
indvdum@0
   367
    }
indvdum@0
   368
indvdum@0
   369
    protected void handleSuggest(final String input) {
indvdum@0
   370
indvdum@0
   371
        final boolean cancelIfNotASingleMatch = (input != null && !input
indvdum@0
   372
                .equals(lastSuggestInput));
indvdum@0
   373
        lastSuggestInput = input;
indvdum@0
   374
indvdum@0
   375
        final Set<String> matches = handler.getSuggestions(this, input);
indvdum@0
   376
indvdum@0
   377
        if (matches == null || matches.size() == 0) {
indvdum@0
   378
            bell();
indvdum@0
   379
            return;
indvdum@0
   380
        }
indvdum@0
   381
indvdum@0
   382
        // Output the original
indvdum@0
   383
        final String prefix = parseCommandPrefix(input);
indvdum@0
   384
        String output = input.substring(0, input.lastIndexOf(prefix));
indvdum@0
   385
        if (matches.size() == 1) {
indvdum@0
   386
            // Output the only match
indvdum@0
   387
            output += matches.iterator().next() + " "; // append the single
indvdum@0
   388
            // match
indvdum@0
   389
        } else {
indvdum@0
   390
indvdum@0
   391
            // We output until the common prefix
indvdum@0
   392
            StringBuilder commonPrefix = new StringBuilder(prefix);
indvdum@0
   393
            final int maxLen = matches.iterator().next().length();
indvdum@0
   394
            for (int i = prefix.length(); i < maxLen; i++) {
indvdum@0
   395
                char c = 0;
indvdum@0
   396
                boolean charMatch = true;
indvdum@0
   397
                for (final String m : matches) {
indvdum@0
   398
                    if (c == 0) {
indvdum@0
   399
                        c = m.charAt(i);
indvdum@0
   400
                    } else if (i < m.length()) {
indvdum@0
   401
                        charMatch &= m.charAt(i) == c;
indvdum@0
   402
                        if (!charMatch) {
indvdum@0
   403
                            break;
indvdum@0
   404
                        }
indvdum@0
   405
                    } else {
indvdum@0
   406
                        charMatch = false;
indvdum@0
   407
                        break;
indvdum@0
   408
                    }
indvdum@0
   409
                }
indvdum@0
   410
                if (charMatch) {
indvdum@0
   411
                    commonPrefix.append(c);
indvdum@0
   412
                }
indvdum@0
   413
            }
indvdum@0
   414
            output += commonPrefix.toString();
indvdum@0
   415
            if (prefix.equals(commonPrefix.toString())
indvdum@0
   416
                    && !cancelIfNotASingleMatch) {
indvdum@0
   417
                final StringBuffer suggestions = new StringBuffer("\n");
indvdum@0
   418
                for (final String m : matches) {
indvdum@0
   419
                    suggestions.append(" " + m);
indvdum@0
   420
                }
indvdum@0
   421
                print(suggestions.toString());
indvdum@0
   422
            } else {
indvdum@0
   423
                bell();
indvdum@0
   424
                lastSuggestInput = output; // next suggest will not beep
indvdum@0
   425
            }
indvdum@0
   426
        }
indvdum@0
   427
        prompt(output);
indvdum@0
   428
        focus();
indvdum@0
   429
indvdum@0
   430
    }
indvdum@0
   431
indvdum@0
   432
    public void bell() {
indvdum@0
   433
        client.call("bell");
indvdum@0
   434
    }
indvdum@0
   435
indvdum@0
   436
    protected void handleInput(final String input) {
indvdum@0
   437
indvdum@0
   438
        // Ask registered handler
indvdum@0
   439
        handler.inputReceived(this, input);
indvdum@0
   440
indvdum@0
   441
    }
indvdum@0
   442
indvdum@0
   443
    protected void parseAndExecuteCommand(final String input) {
indvdum@0
   444
        final String[] argv = parseInput(input);
indvdum@0
   445
        if (argv != null && argv.length > 0) {
indvdum@0
   446
            final Command c = getCommand(argv[0]);
indvdum@0
   447
            if (c != null) {
indvdum@0
   448
                final String result = executeCommand(c, argv);
indvdum@0
   449
                if (result != null) {
indvdum@0
   450
                    print(result);
indvdum@0
   451
                }
indvdum@0
   452
            } else {
indvdum@0
   453
                handler.commandNotFound(this, argv);
indvdum@0
   454
            }
indvdum@0
   455
        }
indvdum@0
   456
    }
indvdum@0
   457
indvdum@0
   458
    protected String executeCommand(final Command cmd, final String[] argv) {
indvdum@0
   459
        try {
indvdum@0
   460
            final Object r = cmd.execute(this, argv);
indvdum@0
   461
            return r != null ? "" + r : null;
indvdum@0
   462
        } catch (final Exception e) {
indvdum@0
   463
            handler.handleException(this, e, cmd, argv);
indvdum@0
   464
        }
indvdum@0
   465
        return null;
indvdum@0
   466
    }
indvdum@0
   467
indvdum@0
   468
    protected String parseCommandPrefix(final String input) {
indvdum@0
   469
        if (input == null) {
indvdum@0
   470
            return null;
indvdum@0
   471
        }
indvdum@0
   472
        if (!input.endsWith(" ")) {
indvdum@0
   473
            final String[] argv = parseInput(input);
indvdum@0
   474
            if (argv != null && argv.length > 0) {
indvdum@0
   475
                return argv[argv.length - 1];
indvdum@0
   476
            }
indvdum@0
   477
        }
indvdum@0
   478
        return "";
indvdum@0
   479
    }
indvdum@0
   480
indvdum@0
   481
    protected static String[] parseInput(final String input) {
indvdum@0
   482
        if (input != null && !"".equals(input.trim())) {
indvdum@0
   483
            final String[] temp = input.split(" ");
indvdum@0
   484
            if (temp != null && temp.length > 0) {
indvdum@0
   485
                final List<String> parsed = new ArrayList<String>(temp.length);
indvdum@0
   486
                String current = null;
indvdum@0
   487
                for (final String element : temp) {
indvdum@0
   488
                    final int quotCount = count(element, '\"');
indvdum@0
   489
                    if (quotCount > 0 && quotCount % 2 != 0) {
indvdum@0
   490
                        // uneven number of quotes star or end combining params
indvdum@0
   491
                        if (current != null) {
indvdum@0
   492
                            parsed.add(current + " "
indvdum@0
   493
                                    + element.replaceAll("\"", "")); // end
indvdum@0
   494
                            current = null;
indvdum@0
   495
                        } else {
indvdum@0
   496
                            current = element.replaceAll("\"", ""); // start
indvdum@0
   497
                        }
indvdum@0
   498
                    } else if (current != null) {
indvdum@0
   499
                        current += " " + element.replaceAll("\"", "");
indvdum@0
   500
                    } else {
indvdum@0
   501
                        parsed.add(element.replaceAll("\"", ""));
indvdum@0
   502
                    }
indvdum@0
   503
                }
indvdum@0
   504
indvdum@0
   505
                // TODO: actually this is not quite right: We have an open quote
indvdum@0
   506
                // somewhere. Exception maybe?
indvdum@0
   507
                if (current != null) {
indvdum@0
   508
                    parsed.add(current.replaceAll("\"", ""));
indvdum@0
   509
                }
indvdum@0
   510
                return parsed.toArray(new String[] {});
indvdum@0
   511
            }
indvdum@0
   512
        }
indvdum@0
   513
        return new String[] {};
indvdum@0
   514
    }
indvdum@0
   515
indvdum@0
   516
    protected static int count(final String sourceString, final char lookFor) {
indvdum@0
   517
        if (sourceString == null) {
indvdum@0
   518
            return -1;
indvdum@0
   519
        }
indvdum@0
   520
        int count = 0;
indvdum@0
   521
        for (int i = 0; i < sourceString.length(); i++) {
indvdum@0
   522
            final char c = sourceString.charAt(i);
indvdum@0
   523
            if (c == lookFor) {
indvdum@0
   524
                count++;
indvdum@0
   525
            }
indvdum@0
   526
        }
indvdum@0
   527
        return count;
indvdum@0
   528
    }
indvdum@0
   529
indvdum@0
   530
    public void print(final String output) {
indvdum@15
   531
    	if(isConvertANSIToCSS){
indvdum@15
   532
    		client.call("print", "");
indvdum@15
   533
    		appendWithProcessingANSICodes(output);
indvdum@15
   534
    	} else
indvdum@15
   535
    		client.call("print", output);
indvdum@0
   536
    }
indvdum@0
   537
indvdum@7
   538
	/**
indvdum@11
   539
	 * Print text with predefined in theme CSS class.
indvdum@11
   540
	 *  
indvdum@7
   541
	 * @param output
indvdum@7
   542
	 * @param className CSS class name for string
indvdum@7
   543
	 */
indvdum@7
   544
	public void print(final String output, final String className) {
indvdum@7
   545
		client.call("print", output, className);
indvdum@7
   546
	}
indvdum@7
   547
indvdum@0
   548
    public String getGreeting() {
indvdum@0
   549
        return config.greeting;
indvdum@0
   550
    }
indvdum@0
   551
indvdum@0
   552
    public String getPs() {
indvdum@0
   553
        return config.ps;
indvdum@0
   554
    }
indvdum@0
   555
indvdum@0
   556
    public int getMaxBufferSize() {
indvdum@0
   557
        return config.maxBufferSize;
indvdum@0
   558
    }
indvdum@0
   559
indvdum@0
   560
    public int getRows() {
indvdum@0
   561
        return config.rows;
indvdum@0
   562
    }
indvdum@0
   563
indvdum@0
   564
    public void setGreeting(final String greeting) {
indvdum@0
   565
        config.greeting = greeting;
indvdum@0
   566
        client.call("setGreeting", greeting);
indvdum@0
   567
    }
indvdum@0
   568
indvdum@0
   569
    public void setPs(final String ps) {
indvdum@0
   570
        config.ps = ps == null ? DEFAULT_PS : ps;
indvdum@0
   571
        client.call("setPs", config.ps);
indvdum@0
   572
    }
indvdum@0
   573
indvdum@0
   574
    public void setMaxBufferSize(final int lines) {
indvdum@0
   575
        config.maxBufferSize = lines > 0 ? lines : 0;
indvdum@0
   576
        client.call("setMaxBufferSize", config.maxBufferSize);
indvdum@0
   577
    }
indvdum@0
   578
indvdum@0
   579
    public void setRows(final int rows) {
indvdum@0
   580
        config.rows = rows;
indvdum@0
   581
        if (config.rows < 1) {
indvdum@0
   582
            config.rows = 1;
indvdum@0
   583
        }
indvdum@0
   584
        if (config.rows > MAX_ROWS) {
indvdum@0
   585
            config.rows = MAX_ROWS;
indvdum@0
   586
        }
indvdum@0
   587
        client.call("setRows", rows);
indvdum@0
   588
    }
indvdum@0
   589
indvdum@0
   590
    public int getCols() {
indvdum@0
   591
        return config.cols;
indvdum@0
   592
    }
indvdum@0
   593
indvdum@0
   594
    public void setCols(final int cols) {
indvdum@0
   595
        config.cols = cols;
indvdum@0
   596
        if (config.cols < 1) {
indvdum@0
   597
            config.cols = 1;
indvdum@0
   598
        }
indvdum@0
   599
        if (config.cols > MAX_COLS) {
indvdum@0
   600
            config.cols = MAX_COLS;
indvdum@0
   601
        }
indvdum@0
   602
        client.call("setCols", config.cols);
indvdum@0
   603
    }
indvdum@0
   604
indvdum@0
   605
    public void prompt() {
indvdum@0
   606
        client.call("prompt");
indvdum@0
   607
    }
indvdum@0
   608
indvdum@0
   609
    public void prompt(final String initialInput) {
indvdum@0
   610
        client.call("prompt", initialInput);
indvdum@0
   611
    }
indvdum@0
   612
indvdum@0
   613
    public void println(final String string) {
indvdum@15
   614
    	if(isConvertANSIToCSS){
indvdum@15
   615
    		client.call("print", "");
indvdum@15
   616
    		appendWithProcessingANSICodes(string + "\n");
indvdum@15
   617
    	} else
indvdum@15
   618
    		client.call("println", string);
indvdum@0
   619
    }
indvdum@7
   620
indvdum@7
   621
	/**
indvdum@11
   622
	 * Print text with predefined in theme CSS class.
indvdum@11
   623
	 * 
indvdum@7
   624
	 * @param string
indvdum@7
   625
	 * @param className CSS class name for string
indvdum@7
   626
	 */
indvdum@7
   627
	public void println(final String string, final String className) {
indvdum@7
   628
		client.call("println", string, className);
indvdum@7
   629
	}
indvdum@3
   630
    
indvdum@10
   631
    /**
indvdum@14
   632
     * @param string text to append to the last printed line
indvdum@14
   633
     * @return this Console object
indvdum@10
   634
     */
indvdum@14
   635
    public Console append(final String string) {
indvdum@15
   636
    	if(isConvertANSIToCSS)
indvdum@15
   637
    		appendWithProcessingANSICodes(string);
indvdum@15
   638
    	else
indvdum@15
   639
    		client.call("append", string);
indvdum@14
   640
    	return this;
indvdum@3
   641
    }
indvdum@15
   642
    
indvdum@15
   643
	private void appendWithProcessingANSICodes(String sOutput) {
indvdum@15
   644
		String splitted[] = sOutput.split(ANSICodeConverter.ANSI_PATTERN);
indvdum@15
   645
		String notPrintedYet = new String(sOutput);
indvdum@15
   646
		for(int i = 0; i < splitted.length; i++){
indvdum@15
   647
			String nextStr = splitted[i];
indvdum@15
   648
			if(i == 0 && nextStr.length() == 0)
indvdum@15
   649
				continue;
indvdum@15
   650
			String cssClasses = "";
indvdum@15
   651
			Pattern firstAnsi = Pattern.compile("^(" + ANSICodeConverter.ANSI_PATTERN + ")+\\Q" + nextStr + "\\E.*", Pattern.DOTALL);
indvdum@15
   652
			if(firstAnsi.matcher(notPrintedYet).matches()){
indvdum@15
   653
				while(firstAnsi.matcher(notPrintedYet).matches()){									
indvdum@15
   654
					String ansi = notPrintedYet.replaceAll("\\Q" + notPrintedYet.replaceAll("^(" + ANSICodeConverter.ANSI_PATTERN + "){1}", "") + "\\E", "");
indvdum@15
   655
					cssClasses += ansiToCSSconverter.convertANSIToCSS(ansi) + " ";
indvdum@15
   656
					notPrintedYet = notPrintedYet.replaceAll("^(" + ANSICodeConverter.ANSI_PATTERN + "){1}", "");
indvdum@15
   657
				}
indvdum@15
   658
				notPrintedYet = notPrintedYet.replaceAll("^\\Q" + nextStr + "\\E", "");
indvdum@15
   659
			} else
indvdum@15
   660
				notPrintedYet = notPrintedYet.replaceFirst("\\Q" + nextStr + "\\E", "");
indvdum@15
   661
			cssClasses = cssClasses.trim();
indvdum@15
   662
			if(cssClasses.length() > 0)
indvdum@15
   663
				client.call("append", nextStr, cssClasses);
indvdum@15
   664
			else
indvdum@15
   665
				client.call("append", nextStr);
indvdum@15
   666
		}
indvdum@15
   667
    }
indvdum@0
   668
indvdum@7
   669
	/**
indvdum@11
   670
	 * Append text with predefined in theme CSS class.
indvdum@11
   671
	 * 
indvdum@10
   672
	 * @param string text to append to the last printed line
indvdum@7
   673
	 * @param className CSS class name for string
indvdum@14
   674
	 * @return this Console object
indvdum@7
   675
	 */
indvdum@14
   676
	public Console append(final String string, final String className) {
indvdum@7
   677
		client.call("append", string, className);
indvdum@14
   678
		return this;
indvdum@7
   679
	}
indvdum@7
   680
indvdum@0
   681
    public void newLine() {
indvdum@0
   682
        client.call("newLine");
indvdum@0
   683
    }
indvdum@0
   684
indvdum@11
   685
	/**
indvdum@11
   686
	 * Print new line only if new line not exists at the end of console
indvdum@11
   687
	 */
indvdum@6
   688
	public void newLineIfNotEndsWithNewLine() {
indvdum@6
   689
		client.call("newLineIfNotEndsWithNewLine");
indvdum@6
   690
	}
indvdum@6
   691
indvdum@0
   692
    public void reset() {
indvdum@0
   693
        client.call("reset");
indvdum@0
   694
    }
indvdum@0
   695
indvdum@0
   696
    public void clear() {
indvdum@0
   697
        formFeed();
indvdum@0
   698
    }
indvdum@0
   699
indvdum@0
   700
    public void formFeed() {
indvdum@0
   701
        client.call("ff");
indvdum@0
   702
    }
indvdum@0
   703
indvdum@0
   704
    public void carriageReturn() {
indvdum@0
   705
        client.call("cr");
indvdum@0
   706
    }
indvdum@0
   707
indvdum@0
   708
    public void lineFeed() {
indvdum@0
   709
        client.call("lf");
indvdum@0
   710
    }
indvdum@0
   711
indvdum@0
   712
    public void clearCommandHistory() {
indvdum@0
   713
        client.call("clearHistory");
indvdum@0
   714
    }
indvdum@0
   715
indvdum@0
   716
    public void clearBuffer() {
indvdum@0
   717
        client.call("clearBuffer");
indvdum@0
   718
    }
indvdum@0
   719
indvdum@0
   720
    public void scrollToEnd() {
indvdum@0
   721
        client.call("scrollToEnd");
indvdum@0
   722
    }
indvdum@1
   723
    
indvdum@10
   724
    /**
indvdum@10
   725
     * Focus input element of console.
indvdum@10
   726
     */
indvdum@1
   727
    public void focusInput() {
indvdum@1
   728
    	client.call("focusInput");
indvdum@1
   729
    }
indvdum@0
   730
indvdum@0
   731
    /**
indvdum@0
   732
     * Gets the Tabulator index of this Focusable component.
indvdum@0
   733
     *
indvdum@0
   734
     * @see com.vaadin.ui.Component.Focusable#getTabIndex()
indvdum@0
   735
     */
indvdum@0
   736
    public int getTabIndex() {
indvdum@0
   737
        return tabIndex;
indvdum@0
   738
    }
indvdum@0
   739
indvdum@0
   740
    /**
indvdum@0
   741
     * Sets the Tabulator index of this Focusable component.
indvdum@0
   742
     *
indvdum@0
   743
     * @see com.vaadin.ui.Component.Focusable#setTabIndex(int)
indvdum@0
   744
     */
indvdum@0
   745
    public void setTabIndex(final int tabIndex) {
indvdum@0
   746
        this.tabIndex = tabIndex;
indvdum@0
   747
    }
indvdum@0
   748
indvdum@0
   749
    /**
indvdum@0
   750
     * {@inheritDoc}
indvdum@0
   751
     */
indvdum@0
   752
    @Override
indvdum@0
   753
    public void focus() {
indvdum@0
   754
        super.focus();
indvdum@0
   755
    }
indvdum@0
   756
indvdum@0
   757
    /* PrintStream implementation for console output. */
indvdum@0
   758
indvdum@0
   759
    public PrintStream getPrintStream() {
indvdum@0
   760
        if (printStream == null) {
indvdum@0
   761
            printStream = new PrintStream(new OutputStream() {
indvdum@0
   762
indvdum@0
   763
                ByteArrayOutputStream buffer = new ByteArrayOutputStream();
indvdum@0
   764
indvdum@0
   765
                @Override
indvdum@0
   766
                public void write(final int b) throws IOException {
indvdum@0
   767
                    buffer.write(b);
indvdum@0
   768
                    // Line buffering
indvdum@0
   769
                    if (13 == b) {
indvdum@0
   770
                        flush();
indvdum@0
   771
                    }
indvdum@0
   772
                }
indvdum@0
   773
indvdum@0
   774
                @Override
indvdum@0
   775
                public void flush() throws IOException {
indvdum@0
   776
                    super.flush();
indvdum@0
   777
                    buffer.flush();
indvdum@0
   778
                    Console.this.print(buffer.toString());
indvdum@0
   779
                    buffer.reset();
indvdum@0
   780
                }
indvdum@0
   781
            }, true);
indvdum@0
   782
        }
indvdum@0
   783
        return printStream;
indvdum@0
   784
    }
indvdum@0
   785
indvdum@0
   786
    /* Generic command handling */
indvdum@0
   787
indvdum@0
   788
    /**
indvdum@0
   789
     * Add a Command to this Console.
indvdum@0
   790
     *
indvdum@0
   791
     * This will override the any commands of the same name available via
indvdum@0
   792
     * {@link CommandProvider}.
indvdum@0
   793
     */
indvdum@0
   794
    public void addCommand(final String name, final Command cmd) {
indvdum@0
   795
        commands.put(name, cmd);
indvdum@0
   796
    }
indvdum@0
   797
indvdum@0
   798
    /**
indvdum@0
   799
     * Remove a command from this console.
indvdum@0
   800
     *
indvdum@0
   801
     * This does not remove Command available from {@link CommandProvider}.
indvdum@0
   802
     *
indvdum@0
   803
     * @param cmdName
indvdum@0
   804
     */
indvdum@0
   805
    public void removeCommand(final String cmdName) {
indvdum@0
   806
        commands.remove(cmdName);
indvdum@0
   807
    }
indvdum@0
   808
indvdum@0
   809
    /**
indvdum@0
   810
     * Get a Command by its name.
indvdum@0
   811
     *
indvdum@0
   812
     * @param cmdName
indvdum@0
   813
     * @return
indvdum@0
   814
     */
indvdum@0
   815
    public Command getCommand(final String cmdName) {
indvdum@0
   816
indvdum@0
   817
        // Try directly registered command first
indvdum@0
   818
        Command cmd = commands.get(cmdName);
indvdum@0
   819
        if (cmd != null) {
indvdum@0
   820
            return cmd;
indvdum@0
   821
        }
indvdum@0
   822
indvdum@0
   823
        // Ask from the providers
indvdum@0
   824
        if (commandProviders != null) {
indvdum@0
   825
            for (final CommandProvider cp : commandProviders) {
indvdum@0
   826
                cmd = cp.getCommand(this, cmdName);
indvdum@0
   827
                if (cmd != null) {
indvdum@0
   828
                    return cmd;
indvdum@0
   829
                }
indvdum@0
   830
            }
indvdum@0
   831
        }
indvdum@0
   832
indvdum@0
   833
        // Not found
indvdum@0
   834
        return null;
indvdum@0
   835
    }
indvdum@0
   836
indvdum@0
   837
    /**
indvdum@0
   838
     * Get the current Console Handler.
indvdum@0
   839
     *
indvdum@0
   840
     * @return
indvdum@0
   841
     */
indvdum@0
   842
    public Handler getHandler() {
indvdum@0
   843
        return handler;
indvdum@0
   844
    }
indvdum@0
   845
indvdum@0
   846
    /**
indvdum@0
   847
     * Set the handler for this console.
indvdum@0
   848
     *
indvdum@0
   849
     * @see Handler
indvdum@0
   850
     * @param handler
indvdum@0
   851
     */
indvdum@0
   852
    public void setHandler(final Handler handler) {
indvdum@0
   853
        this.handler = handler != null ? handler : new DefaultConsoleHandler();
indvdum@0
   854
    }
indvdum@0
   855
indvdum@15
   856
    public ANSICodeConverter getANSIToCSSConverter() {
indvdum@15
   857
		return ansiToCSSconverter;
indvdum@15
   858
	}
indvdum@15
   859
indvdum@15
   860
	public void setANSIToCSSConverter(ANSICodeConverter converter) {
indvdum@15
   861
		this.ansiToCSSconverter = converter != null? converter : new DefaultANSICodeConverter();
indvdum@15
   862
	}
indvdum@15
   863
indvdum@15
   864
	/**
indvdum@15
   865
	 * Converting raw output with ANSI escape sequences to output with CSS-classes.
indvdum@15
   866
	 *
indvdum@15
   867
	 * @return
indvdum@15
   868
	 */
indvdum@15
   869
	public boolean isConvertANSIToCSS() {
indvdum@15
   870
		return isConvertANSIToCSS;
indvdum@15
   871
	}
indvdum@15
   872
indvdum@15
   873
	/**
indvdum@15
   874
	 * Converting raw output with ANSI escape sequences to output with CSS-classes.
indvdum@15
   875
	 *
indvdum@15
   876
	 * @param isConvertANSIToCSS
indvdum@15
   877
	 */
indvdum@15
   878
	public void setConvertANSIToCSS(boolean isConvertANSIToCSS) {
indvdum@15
   879
		this.isConvertANSIToCSS = isConvertANSIToCSS;
indvdum@15
   880
	}
indvdum@15
   881
indvdum@15
   882
	/**
indvdum@0
   883
     * Get map of available commands in this Console.
indvdum@0
   884
     *
indvdum@0
   885
     * @return
indvdum@0
   886
     */
indvdum@0
   887
    public Set<String> getCommands() {
indvdum@0
   888
        final Set<String> res = new HashSet<String>();
indvdum@0
   889
        if (commandProviders != null) {
indvdum@0
   890
            for (final CommandProvider cp : commandProviders) {
indvdum@12
   891
            	if(cp.getAvailableCommands(this) != null)
indvdum@12
   892
            		res.addAll(cp.getAvailableCommands(this));
indvdum@0
   893
            }
indvdum@0
   894
        }
indvdum@0
   895
        res.addAll(commands.keySet());
indvdum@0
   896
        return Collections.unmodifiableSet(res);
indvdum@0
   897
    }
indvdum@0
   898
indvdum@0
   899
}