All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.aesh.readline.Buffer Maven / Gradle / Ivy

There is a newer version: 1.17
Show newest version
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
 * as indicated by the @authors tag. All rights reserved.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * 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.aesh.readline;

import org.aesh.utils.Config;
import org.aesh.readline.util.IntArrayBuilder;
import org.aesh.utils.ANSI;
import org.aesh.readline.util.LoggerUtil;
import org.aesh.readline.util.Parser;
import org.aesh.readline.util.WcWidth;

import java.util.Arrays;
import java.util.function.Consumer;
import java.util.logging.Logger;
import org.aesh.readline.cursor.CursorLocator;

/**
 * Buffer to keep track of text and cursor position in the console.
 * Is using ANSI-codes to clear text and move cursor in the terminal.
 *
 * @author Ståle W. Pedersen
 */
public class Buffer {

    private static final Logger LOGGER = LoggerUtil.getLogger(Buffer.class.getName());

    private int[] line;
    private int cursor;
    private int size;
    private Prompt prompt;
    private int delta; //need to keep track of a delta for ansi terminal
    //if delta happens at the end of the buffer, we can optimize
    //how we update the tty
    private boolean deltaChangedAtEndOfBuffer = true;
    private boolean disablePrompt = false;
    private boolean multiLine = false;
    private int[] multiLineBuffer = new int[0];
    private boolean isPromptDisplayed = false;
    private boolean deletingBackward = true;

    private final CursorLocator locator;

    Buffer() {
        line = new int[1024];
        prompt = new Prompt("");
        locator = new CursorLocator(this);
    }

    Buffer(Prompt prompt) {
        line = new int[1024];
        if(prompt != null)
            this.prompt = prompt;
        else
            this.prompt = new Prompt("");
        locator = new CursorLocator(this);
    }

    public Buffer(Buffer buf) {
        line = buf.line.clone();
        cursor = buf.cursor;
        size = buf.size;
        prompt = buf.prompt.copy();
        locator = new CursorLocator(this);
    }

    public CursorLocator getCursorLocator() {
        return locator;
    }

    public int get(int pos) {
        if(pos > -1 && pos <= size)
            return line[pos];
        else
            throw new IndexOutOfBoundsException();
    }

    public int cursor() {
        return cursor;
    }

    public int multiCursor() {
        if (multiLine) {
            return multiLineBuffer.length + cursor;
        }
        return cursor;
    }

    public boolean isMasking() {
        return prompt.isMasking();
    }

    public boolean isMultiLine() {
        return multiLine;
    }

    public String asString() {
        return Parser.fromCodePoints(multiLine());
    }

    public void reset() {
        cursor = 0;
        for(int i=0; i out, int width) {
        if(prompt != null) {
            delta =  prompt.getLength() - this.prompt.getLength();
            this.prompt = prompt;
            print(out, width);
        }
    }

    public Prompt prompt() {
        return prompt;
    }

    public int length() {
        if(isMasking() && prompt.getMask() == 0)
            return 1;
        else
            return size;
    }

    private int promptLength() {
        return disablePrompt ? 0 : prompt.getLength();
    }

    public void setMultiLine(boolean multi) {
        if(!isMasking())
            multiLine = multi;
    }

    /**
     * Some completion occured, do not try to compute character index location.
     * This could be revisited to implement a strategy.
     */
    public void invalidateCursorLocation() {
        if (isMultiLine()) {
            locator.invalidateCursorLocation();
        }
    }

    public void updateMultiLineBuffer() {
        int originalSize = multiLineBuffer.length;
        // Store the size of each line.
        int cmdSize;
        if (lineEndsWithBackslash()) {
            cmdSize = size - 1;
            multiLineBuffer = Arrays.copyOf(multiLineBuffer, originalSize + size-1);
            System.arraycopy(line, 0, multiLineBuffer, originalSize, size-1);
        }
        //here we have an open quote, so we need to feed a new-line into the buffer
        else {
            cmdSize = size + Config.getLineSeparator().length();
            multiLineBuffer = Arrays.copyOf(multiLineBuffer, originalSize + cmdSize);
            System.arraycopy(line, 0, multiLineBuffer, originalSize, size);
            // add new line
            int[] lineSeparator = Parser.toCodePoints(Config.getLineSeparator());
            System.arraycopy(lineSeparator, 0, multiLineBuffer, originalSize+size, lineSeparator.length);
        }
        locator.addLine(cmdSize, prompt.getLength());
        clear();
        prompt = new Prompt("> ");
        cursor = 0;
        size = 0;
    }

    private boolean lineEndsWithBackslash() {
        return (size > 0 && line[size-1] == '\\');
    }

    /**
     * Insert text at cursor position
     *
     * @param data text
     */
    public void insert(Consumer out, int[] data, int width) {
        doInsert(data);
        printInsertedData(out, width);
    }

    /**
     * Insert at cursor position.
     *
     * @param data char
     */
    public void insert(Consumer out, int data, int width) {
        doInsert(data);
        printInsertedData(out, width);
   }

    private void doInsert(int data) {
        int width = WcWidth.width(data);
        if(width == -1) {
            //todo: handle control chars...
        }
        else if(width == 1) {
            if(cursor < size)
                System.arraycopy(line, cursor, line, cursor + 1, size - cursor);
            line[cursor++] = data;
            size++;
            delta++;
            if(size == line.length)
                line = Arrays.copyOf(line, line.length + line.length/2);

            deltaChangedAtEndOfBuffer = (size == cursor);
        }
    }

    private void doInsert(int[] data) {
        boolean gotControlChar = false;
        for (int aData : data) {
            int width = WcWidth.width(aData);
            if (width == -1) {
                gotControlChar = true;
                //todo: handle control chars...
            }
        }
        if(!gotControlChar) {
            doActualInsert(data);
        }
    }

    private void doActualInsert(int[] data) {
        if(data.length > (line.length-size))
            line = Arrays.copyOf(line, line.length+ data.length +1);
        if(cursor < size)
            System.arraycopy(line, cursor, line, cursor + data.length, size - cursor);
        for (int aData : data)
            line[cursor++] = aData;
        size += data.length;
        delta += data.length;

        deltaChangedAtEndOfBuffer = (size == cursor);
    }

    /**
     * Move the cursor left if the param is negative, right if its positive.
     *
     * @param out stream
     * @param move where to move
     * @param termWidth terminal width
     */
    public void move(Consumer out,  int move, int termWidth) {
        move(out, move, termWidth, false);
    }

    /**
     * Move the cursor left if the param is negative, right if its positive.
     * If viMode is true, the cursor will not move beyond the current buffer size
     *
     * @param out stream
     * @param move where to move
     * @param termWidth terminal width
     * @param viMode edit mode (vi or emacs)
     */
    public void move(Consumer out, int move, int termWidth, boolean viMode) {
        move = calculateActualMovement(move, viMode);
        //quick exit
        if(move == 0)
            return;

        // 0 Masking separates the UI cursor position from the 'real' cursor position.
        // Cursor movement still has to occur, via calculateActualMovement and setCursor above,
        // to put new characters in the correct location in the invisible line,
        // but this method should always return an empty character so the UI cursor does not move.
        if(isMasking() && prompt.getMask() == 0){
            return;
        }

        out.accept( syncCursor(promptLength()+cursor, promptLength()+cursor+move, termWidth));

        cursor = cursor + move;


    }

    private int[] syncCursor(int currentPos, int newPos, int width) {
        IntArrayBuilder builder = new IntArrayBuilder();
        if(newPos < 0)
            newPos = 0;
        if(currentPos / width == newPos / width) {
            if(currentPos > newPos)
                builder.append(moveNumberOfColumns(currentPos-newPos, 'D'));
            else
                builder.append(moveNumberOfColumns(newPos-currentPos, 'C'));
        }
        //if cursor and end of buffer is on different lines, we need to move the cursor
        else {
            int moveToLine = currentPos / width - newPos / width;
            int moveToColumn = currentPos % width - newPos % width;
            char rowDirection = 'A';
            if(moveToLine < 0) {
                rowDirection = 'B';
                moveToLine = Math.abs(moveToLine);
            }
            builder.append(  moveNumberOfColumnsAndRows(  moveToLine, rowDirection, moveToColumn));
        }
        return builder.toArray();
    }

    /**
     * This is a special case when we have a insert and the buffer is at the
     * terminal edge.
     * Move cursor to the correct line if its not on the same line.
     * Move cursor to the beginning of the line, then move it to its correct position
     *
     * @param currentPos current position
     * @param newPos end position
     * @param width terminal width
     * @return out buffer
     */
    private int[] syncCursorWhenBufferIsAtTerminalEdge(int currentPos, int newPos, int width) {
        IntArrayBuilder builder = new IntArrayBuilder();

         if((currentPos-1) / width == newPos / width) {
             builder.append(moveNumberOfColumns(width, 'D'));
        }
        else {
             //if cursor and end of buffer is on different lines, we need to move the cursor
             int moveToLine = (currentPos - 1) / width - newPos / width;
             int moveToColumn = -currentPos;
             char rowDirection = 'A';
             if (moveToLine < 0) {
                 rowDirection = 'B';
                 moveToLine = Math.abs(moveToLine);
             }

             builder.append(moveNumberOfColumnsAndRows(moveToLine, rowDirection, width));
         }
         //now the cursor should be on the correct line and at position 0
        // we then need to move it to newPos
        builder.append(moveNumberOfColumns(newPos % width, 'C'));
        return builder.toArray();
    }

    public int[] moveNumberOfColumns(int column, char direction) {
        if(column < 10) {
            int[] out = new int[4];
            out[0] = 27; // esc
            out[1] = '['; // [
            out[2] = 48 + column;
            out[3] = direction;
            return out;
        }
        else {
            int[] asciiColumn = intToAsciiInts(column);
            int[] out = new int[3+asciiColumn.length];
            out[0] = 27; // esc
            out[1] = '['; // [
            //for(int i=0; i < asciiColumn.length; i++)
            //    out[2+i] = asciiColumn[i];
            System.arraycopy(asciiColumn, 0, out, 2, asciiColumn.length);
            out[out.length-1] = direction;
            return out;
        }
    }

    private int[] moveNumberOfColumnsAndRows(int row, char rowCommand, int column) {
        char direction = 'D'; //forward
        if(column < 0) {
            column = Math.abs(column);
            direction = 'C';
        }
        if(row < 10 && column < 10) {
            int[] out = new int[8];
            out[0] = 27; //esc, \033
            out[1] = '[';
            out[2] = 48 + row;
            out[3] = rowCommand;
            out[4] = 27;
            out[5] = '[';
            out[6] = 48 + column;
            out[7] = direction;
            return out;
        }
        else {
            int[] asciiRow = intToAsciiInts(row);
            int[] asciiColumn = intToAsciiInts(column);
            int[] out = new int[6+asciiColumn.length+asciiRow.length];
            out[0] = 27; //esc, \033
            out[1] = '[';
            //for(int i=0; i < asciiRow.length; i++)
            //    out[2+i] = asciiRow[i];
            System.arraycopy(asciiRow, 0, out, 2, asciiRow.length);
            out[2+asciiRow.length] = rowCommand;
            out[3+asciiRow.length] = 27;
            out[4+asciiRow.length] = '[';
            for(int i=0; i < asciiColumn.length; i++)
                out[5+asciiRow.length+i] = asciiColumn[i];
            out[out.length-1] = direction;
            return out;
        }
    }

    /**
     * Make sure that the cursor do not move ob (out of bounds)
     *
     * @param move left if its negative, right if its positive
     * @param viMode if viMode we need other restrictions compared
     * to emacs movement
     * @return adjusted movement
     */
    private int calculateActualMovement(final int move, boolean viMode) {
        // cant move to a negative value
        if(cursor() == 0 && move <=0 )
            return 0;
        // cant move longer than the length of the line
        if(viMode) {
            if(cursor() == length()-1 && (move > 0))
                return 0;
        }
        else {
            if(cursor() == length() && (move > 0))
                return 0;
        }

        // dont move out of bounds
        if(cursor() + move <= 0)
            return -cursor();

        if(viMode) {
            if(cursor() + move > length()-1)
                return (length()-1- cursor());
        }
        else {
            if(cursor() + move > length())
                return (length()- cursor());
        }

        return move;
    }

    private int[] getLineFrom(int position) {
        return Arrays.copyOfRange(line, position, size);
    }

    public int[] getLineMasked() {
        if(!isMasking())
            return Arrays.copyOf(line, size);
        else {
            if(size > 0 && prompt.getMask() != '\u0000') {
                int[] tmpLine = new int[size];
                Arrays.fill(tmpLine, prompt.getMask());
                return tmpLine;
            }
            else
                return new int[0];
        }
    }

    private int[] getLine() {
        return Arrays.copyOf(line, size);
    }

    public void clear() {
        Arrays.fill(this.line, 0, size, 0);
        cursor = 0;
        size = 0;
        isPromptDisplayed = false;
    }

    /**
     * If delta > 0 * print from cursor
     *   if keepCursor, move cursor back to previous position
     * if delta < 0
     *   if deltaChangedAtEndOf buffer {
     *       if delta == -1 {
     *         clear line from cursor
     *         move cursor back
     *       }
     *       else if cursor + delta > width {
     *           check if we need to delete more than the current line
     *       }
     *   }
     * @param out output
     * @param width terminal size
     */
    void print(Consumer out, int width) {
        print(out, width, false);
    }

    private void print(Consumer out, int width, boolean viMode) {
        if(delta >= 0)
            printInsertedData(out, width);
        else {
            printDeletedData(out, width, viMode);
        }
        delta = 0;
    }

    private void printInsertedData(Consumer out, int width) {
        //print out prompt first if needed
        IntArrayBuilder builder = new IntArrayBuilder();
        if(!isPromptDisplayed) {
            //only print the prompt if its longer than 0
            if(promptLength() > 0)
                builder.append(prompt.getANSI());
            isPromptDisplayed = true;
            //need to print the entire buffer
            //force that by setting delta = cursor if delta is 0
            if(delta == 0)
                delta = cursor;
        }
        //quick exit if buffer is empty
        if(size == 0) {
            out.accept(builder.toArray());
            return;
        }

        if(isMasking()) {
            if(prompt.getMask() != 0) {
                int[] mask = new int[delta];
                Arrays.fill(mask, prompt.getMask());
                builder.append(mask);
            }
            //a quick exit if we're masking with a no output mask
            else {
                out.accept(builder.toArray());
                delta = 0;
                deltaChangedAtEndOfBuffer = true;
            }
        }
        else {
            if (deltaChangedAtEndOfBuffer) {
                if (delta == 1 || delta == 0) {
                    if(cursor > 0)
                        builder.append(new int[]{line[cursor - 1]});
                    else
                        builder.append(new int[]{line[0]});
                }
                else
                    builder.append(Arrays.copyOfRange(line, cursor - delta, cursor));
            } else {
                builder.append(Arrays.copyOfRange(line, cursor - delta, size));
            }
        }

        //pad if we are at the end of the terminal
        if((size + promptLength()) % width == 0 && deltaChangedAtEndOfBuffer) {
            builder.append(new int[]{32, 13});
        }
        //make sure we sync the cursor back
        if(!deltaChangedAtEndOfBuffer) {
            if((size + promptLength()) % width == 0 && Config.isOSPOSIXCompatible()) {
                builder.append(syncCursorWhenBufferIsAtTerminalEdge(size + promptLength(), cursor + promptLength(), width));
            }
            else
                builder.append(syncCursor(size+promptLength(), cursor+promptLength(), width));
        }

        out.accept(builder.toArray());
        delta = 0;
        deltaChangedAtEndOfBuffer = true;
    }

    private void printDeletedData(Consumer out, int width, boolean viMode) {
        //if we're masking and the mask is no output we just return
        if(isMasking() && prompt.getMask() == 0)
            return;
        IntArrayBuilder builder = new IntArrayBuilder();
         if(size+promptLength()+Math.abs(delta) >= width) {
            if(deletingBackward) {
                //lets optimize deletes at the end
                if(deltaChangedAtEndOfBuffer &&
                        ((size+promptLength()+1) % width > Math.abs(delta))) {
                    quickDeleteAtEnd(out, viMode);
                    return;
                }
                else {
                    clearAllLinesAndReturnToFirstLine(builder,
                            width, cursor + promptLength() + Math.abs(delta),
                            size + promptLength() + Math.abs(delta));
                }
            }
            else
                clearAllLinesAndReturnToFirstLine(builder,
                        width, cursor + promptLength(),
                        size + promptLength() + Math.abs(delta));
        }

        if((size+promptLength()+1) < width && deltaChangedAtEndOfBuffer)
             quickDeleteAtEnd(out, viMode);
        else
            moveCursorToStartAndPrint(out, builder, width, false, viMode);
    }

    private void quickDeleteAtEnd(Consumer out, boolean viMode) {
        //move cursor delta then clear the rest of the line
        IntArrayBuilder builder = new IntArrayBuilder();
        //only have to move when deleting backwards
        if(deletingBackward)
            builder.append(moveNumberOfColumns(Math.abs(delta), 'D'));
        builder.append(ANSI.ERASE_LINE_FROM_CURSOR);

        if(viMode && cursor == size) {
            builder.append(moveNumberOfColumns(1, 'D'));
            cursor--;
        }

        out.accept(builder.toArray());
    }

    /**
     * Replace the entire current buffer with the given line.
     * The new line will be pushed to the consumer
     * Cursor will be moved to the end of the new buffer line
     *
     * @param out stream
     * @param line new buffer line
     * @param width term width
     */
    public void replace(Consumer out, String line, int width) {
        replace(out, Parser.toCodePoints(line), width);
    }

    public void replace(Consumer out, int[] line, int width) {
        //quick exit
        if(line == null || size == 0 && line.length == 0)
            return;

        int tmpDelta = line.length - size;
        int oldSize = size+promptLength();
        int oldCursor = cursor + promptLength();
        clear();
        doInsert(line);
        delta = tmpDelta;
        //deltaChangedAtEndOfBuffer = false;
        deltaChangedAtEndOfBuffer = (cursor == size);

        IntArrayBuilder builder = new IntArrayBuilder();
        if(oldSize >= width)
            clearAllLinesAndReturnToFirstLine(builder, width, oldCursor, oldSize);

        moveCursorToStartAndPrint(out, builder, width, true, false);
        delta = 0;
        deltaChangedAtEndOfBuffer = true;
    }

    /**
     * All parameter values are included the prompt length
     * @param builder int[] builder
     * @param width terminal size
     * @param oldCursor prev position
     * @param oldSize prev terminal size
     */
    private void clearAllLinesAndReturnToFirstLine(IntArrayBuilder builder, int width,
                                                   int oldCursor, int oldSize) {
        if(oldSize >= width) {
            int cursorRow = oldCursor / width;
            int totalRows = oldSize / width;

            if((oldSize) % width == 0 && oldSize == oldCursor) {
                cursorRow = (oldCursor-1) / width;
                builder.append(ANSI.MOVE_LINE_UP);
            }
            //if total row > cursor row it means that the cursor is not at the last line of the row
            //then we need to move down number of rows first
            //TODO: we can optimize here by going the number of rows down in one step
            if(totalRows > cursorRow && delta < 0) {
                for(int i=0; i < (totalRows-cursorRow); i++) {
                    builder.append(ANSI.MOVE_LINE_DOWN);
                }
                for (int i = 0; i < totalRows; i++) {
                    builder.append(ANSI.ERASE_WHOLE_LINE);
                    builder.append(ANSI.MOVE_LINE_UP);
                }
            }
            else {
                for (int i = 0; i < cursorRow; i++) {
                    if (delta < 0) {
                        builder.append(ANSI.ERASE_WHOLE_LINE);
                    }
                    builder.append(ANSI.MOVE_LINE_UP);
                }
            }
        }
    }

    private void moveCursorToStartAndPrint(Consumer out, IntArrayBuilder builder,
                                           int width, boolean replace, boolean viMode) {

        if(cursor > 0 || delta < 0) {
            //if we replace we do a quick way of moving to the beginning
            if(replace) {
                builder.append(moveNumberOfColumns(width, 'D'));
            }
            else {
                int length = promptLength() + cursor;
                if(length > 0 && (length % width == 0))
                    length = width;
                else {
                    length = length % width;
                    //if not deleting backward the cursor should not move
                    if(delta < 0 && deletingBackward)
                        length += Math.abs(delta);
                }
                builder.append(moveNumberOfColumns(length, 'D'));
            }
            //TODO: could optimize this i think if delta > 0 it should not be needed
            builder.append(ANSI.ERASE_LINE_FROM_CURSOR);
        }

        if(promptLength() > 0)
            builder.append(prompt.getANSI());

        //dont print out the line if its empty
        if(size > 0) {
            if(isMasking()) {
                //no output
                if(prompt.getMask() != '\u0000') {
                    //only output the masked char
                    int[] mask = new int[size];
                    Arrays.fill(mask, prompt.getMask());
                    builder.append(mask);
                }
            }
            else
                builder.append(getLine());
        }

        //pad if we are at the end of the terminal
        if((size + promptLength()) % width == 0 && cursor == size) {
            builder.append(new int[]{32, 13});
        }
        //make sure we sync the cursor back
        if(!deltaChangedAtEndOfBuffer) {
            if((size + promptLength()) % width == 0 && Config.isOSPOSIXCompatible())
                builder.append(syncCursor(size+promptLength()-1, cursor+promptLength(), width));
            else
                builder.append(syncCursor(size+promptLength(), cursor+promptLength(), width));
         }
        //end of buffer and vi mode
        else if(viMode && cursor == size) {
            builder.append(moveNumberOfColumns(1, 'D'));
            cursor--;
        }

        out.accept(builder.toArray());
        isPromptDisplayed = true;
    }

    public int[] multiLine() {
        if (multiLine) {
            int[] tmpLine = Arrays.copyOf(multiLineBuffer, multiLineBuffer.length + size);
            System.arraycopy(line, 0, tmpLine, multiLineBuffer.length, size);
            return  tmpLine;
        }
        else {
            return getLine();
        }
    }

    /**
     * Delete from cursor position and backwards if delta is < 0
     * Delete from cursor position and forwards if delta is > 0
     * @param delta difference
     */
    public void delete(Consumer out, int delta, int width) {
       delete(out, delta, width, false);
    }

    public void delete(Consumer out, int delta, int width, boolean viMode) {
        if (delta > 0) {
            delta = Math.min(delta, size - cursor);
            if(delta > 0) {
                System.arraycopy(line, cursor + delta, line, cursor, size - cursor + delta);
                size -= delta;
                this.delta = -delta;
                deletingBackward = false;
            }
            //quick return if delta is 0
            else
                return;
        }
        else if (delta < 0) {
            delta = -Math.min(-delta, cursor);
            System.arraycopy(line, cursor, line, cursor + delta, size - cursor);
            size += delta;
            cursor += delta;
            this.delta =+ delta;
            deletingBackward = true;
        }

        //only do any changes if there are any
        if(this.delta < 0) {
            // Erase the remaining.
            Arrays.fill(line, size, line.length, 0);

            deltaChangedAtEndOfBuffer = (cursor == size);

            //finally print our changes
            print(out, width, viMode);
        }
    }

    /**
     * Write a string to the line and update cursor accordingly
     *
     * @param out consumer
     * @param str string
     */
    public void insert(Consumer out, final String str, int width) {
        insert(out, Parser.toCodePoints(str), width);
    }

    /**
     * Switch case if the current character is a letter.
     */
    void changeCase(Consumer out) {
        if(Character.isLetter(line[cursor])) {
            if(Character.isLowerCase(line[cursor]))
                line[cursor] = Character.toUpperCase(line[cursor]);
            else
                line[cursor] = Character.toLowerCase(line[cursor]);

            out.accept(new int[]{line[cursor]});
        }
    }

    /**
     * Up case if the current character is a letter
     */
    void upCase(Consumer out) {
        if(Character.isLetter(line[cursor])) {
            line[cursor] = Character.toUpperCase(line[cursor]);
            out.accept(new int[]{line[cursor]});
        }
    }

    /**
     * Lower case if the current character is a letter
     */
    void downCase(Consumer out) {
        if(Character.isLetter(line[cursor])) {
            line[cursor] = Character.toLowerCase(line[cursor]);
            out.accept(new int[]{line[cursor]});
        }
    }

    /**
     * Replace the current character
     */
    public void replace(Consumer out, char rChar) {
        doReplace(out, cursor(), rChar);
    }

    private void doReplace(Consumer out, int pos, int rChar) {
        if(pos > -1 && pos <= size) {
            line[pos] = rChar;
            out.accept(new int[]{rChar});
        }
    }

    /**
     * we assume that value is > 0
     *
     * @param value int value (non ascii value)
     * @return ascii represented int value
     */
    private int[] intToAsciiInts(int value) {
        int length = getAsciiSize(value);
        int[] asciiValue = new int[length];

        if(length == 1) {
            asciiValue[0] = 48+value;
        }
        else {
            while(length > 0) {
                length--;
                int num = value % 10;
                asciiValue[length] = 48+num;
                value = value / 10;
            }
        }
        return asciiValue;
    }

    private int getAsciiSize(int value) {
        if(value < 10)
            return 1;
        //very simple way of getting the length
        if(value > 9 && value < 99)
            return 2;
        else if(value > 99 && value < 999)
            return 3;
        else if(value > 999 && value < 9999)
            return 4;
        else
            return 5;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy