/** * Copyright 2009 Google Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.waveprotocol.box.consoleclient; import com.google.common.collect.ImmutableList; import jline.ANSIBuffer; import org.waveprotocol.box.common.DocumentConstants; import org.waveprotocol.wave.model.document.operation.AnnotationBoundaryMap; import org.waveprotocol.wave.model.document.operation.Attributes; import org.waveprotocol.wave.model.document.operation.DocInitializationCursor; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.document.operation.impl.DocOpBuilder; import org.waveprotocol.wave.model.document.operation.impl.InitializationCursorAdapter; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Utility methods and constants for use with the console client. * * */ public class ConsoleUtils { /** ANSI code for text with no attributes. */ public static final int ANSI_NO_ATTRS = 0; /** ANSI code for bold text. */ public static final int ANSI_BOLD = 1; /** ANSI code for underlined text. */ public static final int ANSI_UNDERLINE = 4; /** ANSI code for red foreground text. */ public static final int ANSI_RED_FG = 31; /** ANSI code for green foreground text. */ public static final int ANSI_GREEN_FG = 32; /** ANSI code for yellow foreground text. */ public static final int ANSI_YELLOW_FG = 33; /** ANSI code for blue foreground text. */ public static final int ANSI_BLUE_FG = 34; /** ANSI code for cyan foreground text. */ public static final int ANSI_CYAN_FG = 36; /** ANSI code for white foreground text. */ public static final int ANSI_WHITE_FG = 37; /** ANSI code for green background text. */ public static final int ANSI_GREEN_BG = 42; /** ANSI code for blue background text. */ public static final int ANSI_BLUE_BG = 44; /** ANSI code for cyan background text. */ public static final int ANSI_CYAN_BG = 46; /** ANSI code for white background text. */ public static final int ANSI_WHITE_BG = 47; private ConsoleUtils() { } /** * Ensure the width of a StringBuilder by dropping characters at the end or filling with spaces. * * @param width to set the string buffer to * @param builder to ensure length of */ public static void ensureWidth(int width, StringBuilder builder) { if (builder.length() > width) { builder.delete(width, builder.length()); } else { while (builder.length() < width) { builder.append(' '); } } } /** * Ensure the width of a String by dropping characters at the end or filling with spaces. * * @param width of the new String * @param string to ensure length of * @return String guaranteed to be of given width */ public static String ensureWidth(int width, String string) { StringBuilder builder = new StringBuilder(string); ensureWidth(width, builder); return builder.toString(); } /** * Create a blank line of a given width. * * @param width of the blank line * @return blank line of given width */ public static String blankLine(int width) { return ensureWidth(width, ""); } /** * Ensure the "height" of a number of lines by adding blank lines to the end. * * @param width each line should be (for filling in missing lines) * @param height * @param lines to ensure height of */ public static void ensureHeight(int width, int height, List<String> lines) { while (lines.size() < height) { lines.add(blankLine(width)); } } /** * Wrap a string in a list of ANSI escape codes, then reset at the end. * * @param ansiCodes to apply to the string * @param string to apply the codes to * @return string with applied ANSI codes */ public static String ansiWrap(List<Integer> ansiCodes, String string) { StringBuilder builder = new StringBuilder(string); for (Integer code : ansiCodes) { builder.insert(0, ANSIBuffer.ANSICodes.attrib(code)); } builder.append(ANSIBuffer.ANSICodes.attrib(ANSI_NO_ATTRS)); return builder.toString(); } /** * Wrap a string in a single ANSI escape code, then reset at the end. * * @param ansiCode to apply to the string * @param string to apply the code to * @return string with applied ANSI code */ public static String ansiWrap(int ansiCode, String string) { return ansiWrap(ImmutableList.of(ansiCode), string); } /** * Render a String "nicely" by replacing new lines and tabs with spaces (etc). * * @param string to render nicely * @return nice version of string */ public static String renderNice(String string) { string = string.replaceAll("\n", " "); string = string.replaceAll("\t", " "); return string; } /** * Delete a line from a document. * * Each line is indicated by an start/end XML line tag, followed by text content until the next * start/end XML line tag or the end of the document. The start/end tags as well as line contents * will be deleted by the generated operation. * * @param doc to delete line from * @param lineNumber of line (as an index) to delete * @return operation to delete a line from a document */ public static DocOp createLineDeletion(final DocOp doc, final int lineNumber) { final DocOpBuilder lineDeletion = new DocOpBuilder(); final AtomicInteger currentLine = new AtomicInteger(-1); doc.apply(InitializationCursorAdapter.adapt(new DocInitializationCursor() { @Override public void characters(String s) { if (currentLine.get() == lineNumber) { lineDeletion.deleteCharacters(s); } else { lineDeletion.retain(s.length()); } } @Override public void elementStart(String key, Attributes attrs) { if (key.equals(DocumentConstants.LINE)) { currentLine.incrementAndGet(); } if (currentLine.get() == lineNumber) { lineDeletion.deleteElementStart(key, attrs); } else { lineDeletion.retain(1); } } @Override public void elementEnd() { if (currentLine.get() == lineNumber) { lineDeletion.deleteElementEnd(); } else { lineDeletion.retain(1); } } @Override public void annotationBoundary(AnnotationBoundaryMap map) {} })); return lineDeletion.build(); } }