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