Package org.apache.blur.shell

Source Code of org.apache.blur.shell.TableDisplay$LineBuilder

/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.blur.shell;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;

import jline.Terminal;
import jline.console.ConsoleReader;

public class TableDisplay implements Closeable {

  public static void main(String[] args) throws IOException, InterruptedException {

    // System.out.println("\u001B[1mfoo\u001B[0m@bar\u001B[32m@baz\u001B[0m>");

    // \u001B[0m normal
    // \u001B[33m yellow

    // System.out.println("\u001B[33mCOLUMN\u001B[0mnormal");
    //
    // for (int i = 0; i < 100; i++) {
    // System.out.println("\u001B[0m" + i + "\u001B[" + i + "mfoo");
    // }

    ConsoleReader reader = new ConsoleReader();
    TableDisplay tableDisplay = new TableDisplay(reader);
    tableDisplay.setSeperator("|");
    // Random random = new Random();
    int maxX = 20;
    int maxY = 100;
    tableDisplay.setHeader(0, "");
    for (int i = 1; i < maxX; i++) {
      tableDisplay.setHeader(i, "col-" + i);
    }

    for (int i = 0; i < maxY; i++) {
      tableDisplay.set(0, i, i);
    }

    final AtomicBoolean running = new AtomicBoolean(true);

    tableDisplay.addKeyHook(new Runnable() {
      @Override
      public void run() {
        synchronized (running) {
          running.set(false);
          running.notifyAll();
        }
      }
    }, 'q');

    try {
      // int i = 0;
      // while (true) {
      // if (i >= 100000) {
      // i = 0;
      // }
      // tableDisplay.set(random.nextInt(maxX) + 1, random.nextInt(maxY),
      // random.nextLong());
      // Thread.sleep(3000);
      // i++;
      // }
      for (int x = 0; x < maxX; x++) {
        for (int y = 0; y < maxY; y++) {
          if (x == 7 && y == 7) {
            tableDisplay.set(x, y, highlight(x + "," + y));
          } else {
            tableDisplay.set(x, y, x + "," + y);
          }
        }
      }
      while (running.get()) {
        synchronized (running) {
          running.wait(1000);
        }
      }
    } finally {
      tableDisplay.close();
    }
  }

  private static String highlight(String s) {
    // return s;
    return "\u001B[33m" + s + "\u001B[0m";
  }

  public void addKeyHook(Runnable runnable, int c) {
    _keyHookMap.put(c, runnable);
  }

  private static final String SEP = "|";
  private final ConsoleReader _reader;
  private final ConcurrentMap<Key, Object> _values = new ConcurrentHashMap<TableDisplay.Key, Object>();
  private final ConcurrentMap<Integer, String> _header = new ConcurrentHashMap<Integer, String>();
  private final ConcurrentMap<Integer, Integer> _maxWidth = new ConcurrentHashMap<Integer, Integer>();
  private final AtomicBoolean _running = new AtomicBoolean(true);
  private final Thread _paintThread;
  private final Canvas _canvas;
  private int _maxY;
  private int _maxX;
  private String _seperator = SEP;
  private final Thread _inputReaderThread;
  private final Map<Integer, Runnable> _keyHookMap = new ConcurrentHashMap<Integer, Runnable>();
  private final AtomicBoolean _stopReadingInput = new AtomicBoolean(false);
  private String _description = "";
  private volatile long _lastPaint = 0;
  private volatile boolean _dirty = true;

  public void setDescription(String description) {
    _description = description;
  }

  public void setSeperator(String seperator) {
    _seperator = seperator;
  }

  public TableDisplay(ConsoleReader reader) {
    _reader = reader;
    _canvas = new Canvas(_reader);
    _paintThread = new Thread(new Runnable() {
      @Override
      public void run() {
        while (_running.get()) {
          long now = System.currentTimeMillis();
          synchronized (_canvas) {
            if (_lastPaint + 1000 < now) {
              try {
                render(_canvas);
              } catch (IOException e) {
                e.printStackTrace();
              }
              _lastPaint = System.currentTimeMillis();
              _dirty = false;
            }
            if (_dirty) {
              try {
                _canvas.wait(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            } else {
              try {
                _canvas.wait();
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
            }
          }
        }
      }
    });
    _paintThread.setDaemon(true);
    _paintThread.setName("Render Thread");
    _paintThread.start();
    _inputReaderThread = new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          InputStream input = _reader.getInput();
          int read;
          while (!_stopReadingInput.get() && _running.get() && (read = input.read()) != -1) {
            if (read == 27) {
              if (input.read() == 91) {
                read = input.read();
                switch (read) {
                case 68:
                  // left
                  _canvas.moveLeft();
                  break;
                case 67:
                  // right
                  _canvas.moveRight();
                  break;
                case 65:
                  // up
                  _canvas.moveUp();
                  break;
                case 66:
                  // down
                  _canvas.moveDown();
                  break;
                case 53:
                  // up
                  _canvas.pageUp();
                  break;
                case 54:
                  // down
                  _canvas.pageDown();
                  break;
                default:
                  break;
                }
                _lastPaint = 0;
              }
            } else {
              Runnable runnable = _keyHookMap.get(read);
              if (runnable != null) {
                runnable.run();
              }
            }
            paint();
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    });
    _inputReaderThread.setName("Input Reader Thread");
    _inputReaderThread.setDaemon(true);
    _inputReaderThread.start();
  }

  private void render(Canvas canvas) throws IOException {
    canvas.reset();
    canvas.resetHeader();
    canvas.appendHeader(_description);
    canvas.endHeader();
    buildHeaderOutput(canvas);
    buildTableOutput(canvas);
    canvas.write();
  }

  private void buildHeaderOutput(Canvas canvas) {
    canvas.startHeader();
    for (int x = 0; x < _maxX; x++) {
      if (x != 0) {
        canvas.appendHeader(_seperator);
      }
      Object value = _header.get(x);
      int widthForColumn = getWidthForColumn(x);
      bufferHeader(canvas, getString(value), widthForColumn);
    }
    canvas.endHeader();
  }

  private void buildTableOutput(Canvas canvas) {
    for (int y = 0; y < _maxY; y++) {
      canvas.startLine();
      for (int x = 0; x < _maxX; x++) {
        if (x != 0) {
          canvas.append(_seperator);
        }
        Key key = new Key(x, y);
        Object value = _values.get(key);
        int widthForColumn = getWidthForColumn(x);
        buffer(canvas, getString(value), widthForColumn);
      }
      canvas.endLine();
    }
  }

  private int getWidthForColumn(int x) {
    Integer width = _maxWidth.get(x);
    if (width == null) {
      return 0;
    }
    return width;
  }

  private void bufferHeader(Canvas canvas, String value, int width) {
    canvas.appendHeader(value);
    width -= getVisibleLength(value);
    while (width > 0) {
      canvas.appendHeader(' ');
      width--;
    }
  }
 
  private void buffer(Canvas canvas, String value, int width) {
    canvas.append(value);
    width -= getVisibleLength(value);
    while (width > 0) {
      canvas.append(' ');
      width--;
    }
  }

  public void setHeader(int x, String name) {
    _header.put(x, name);
    setMaxWidth(x, name);
    paint();
  }

  public void set(int x, int y, Object value) {
    if (value == null) {
      remove(x, y);
      return;
    }
    _values.put(new Key(x, y), value);
    setMaxX(x);
    setMaxY(y);
    setMaxWidth(x, value);
    paint();
  }

  private void paint() {
    synchronized (_canvas) {
      _canvas.notify();
      _dirty = true;
    }
  }

  private void setMaxWidth(int x, Object value) {
    if (value != null) {
      String s = getString(value);
      int length = getVisibleLength(s);
      while (true) {
        Integer width = _maxWidth.get(x);
        if (width == null) {
          width = _maxWidth.putIfAbsent(x, length);
          if (width == null) {
            return;
          }
        }
        if (width < length) {
          if (_maxWidth.replace(x, width, length)) {
            return;
          }
        } else {
          return;
        }
      }
    }
  }

  private int getVisibleLength(String s) {
    int length = 0;
    boolean color = false;
    for (int i = 0; i < s.length(); i++) {
      char c = s.charAt(i);
      if (color) {
        if (c == 'm') {
          color = false;
        }
      } else {
        if (c == 27) {
          color = true;
        } else {
          length++;
        }
      }
    }
    return length;
  }

  private String getString(Object value) {
    if (value != null) {
      return value.toString();
    }
    return "";
  }

  private void setMaxY(int y) {
    if (_maxY < y + 1) {
      _maxY = y + 1;
    }
  }

  private void setMaxX(int x) {
    if (_maxX < x + 1) {
      _maxX = x + 1;
    }
  }

  public void remove(int x, int y) {
    _values.remove(new Key(x, y));
  }

  @Override
  public void close() throws IOException {
    if (_running.get()) {
      _running.set(false);
      _inputReaderThread.interrupt();
      try {
        _inputReaderThread.join();
      } catch (InterruptedException e) {
        throw new IOException(e);
      }
    }
  }

  public static class Key implements Comparable<Key> {

    final int _x;
    final int _y;

    public Key(int x, int y) {
      _x = x;
      _y = y;
    }

    public int getX() {
      return _x;
    }

    public int getY() {
      return _y;
    }

    @Override
    public int compareTo(Key o) {
      if (_y == o._y) {
        return _x - o._x;
      }
      return _y - o._y;
    }

    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      result = prime * result + _x;
      result = prime * result + _y;
      return result;
    }

    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      Key other = (Key) obj;
      if (_x != other._x)
        return false;
      if (_y != other._y)
        return false;
      return true;
    }
  }

  public static class LineBuilder {

    static class LineBuilderPart {
      String _visible;
      String _notVisible;
    }

    private List<LineBuilderPart> _parts = new ArrayList<LineBuilderPart>();
    private boolean _readingColor;
    private StringBuilder _currentNotVisible = new StringBuilder();
    private StringBuilder _currentVisible = new StringBuilder();

    public void reset() {
      _currentNotVisible.setLength(0);
      _currentVisible.setLength(0);
      _parts.clear();
    }

    public void close() {
      startNewPart();
    }

    public int visibleLength() {
      int l = 0;
      for (LineBuilderPart p : _parts) {
        l += p._visible.length();
      }
      return l;
    }

    public String substring(int start, int end) {
      StringBuilder b = new StringBuilder();
      Iterator<LineBuilderPart> iterator = _parts.iterator();
      LineBuilderPart current = iterator.next();
      int partPos = 0;
      boolean currentPartHasEmittedNonVisible = false;
      for (int i = 0; i < end; i++) {
        if (partPos >= current._visible.length()) {
          current = iterator.next();
          partPos = 0;
          currentPartHasEmittedNonVisible = false;
        }
        if (isVisible(i, start, end)) {
          if (!currentPartHasEmittedNonVisible) {
            b.append(current._notVisible);
            currentPartHasEmittedNonVisible = true;
          }
          b.append(current._visible.charAt(partPos));
        }
        partPos++;
      }
      return b.toString();
    }

    private boolean isVisible(int i, int start, int end) {
      if (i >= start && i < end) {
        return true;
      }
      return false;
    }

    public void append(char c) {
      if (_readingColor) {
        if (c == 'm') {
          _readingColor = false;
          _currentNotVisible.append(c);
        } else {
          _currentNotVisible.append(c);
        }
      } else {
        if (c == 27) {
          startNewPart();
          _readingColor = true;
          _currentNotVisible.append(c);
        } else {
          _currentVisible.append(c);
        }
      }
    }

    private void startNewPart() {
      LineBuilderPart part = new LineBuilderPart();
      part._notVisible = _currentNotVisible.toString();
      part._visible = _currentVisible.toString();
      _parts.add(part);
      _currentNotVisible.setLength(0);
      _currentVisible.setLength(0);
    }

    public void append(String s) {
      for (int i = 0; i < s.length(); i++) {
        append(s.charAt(i));
      }
    }

  }

  static class Canvas {

    private final StringBuilder _screenBuilder = new StringBuilder();
    private final LineBuilder _lineBuilder = new LineBuilder();
    private final StringBuilder _headerScreenBuilder = new StringBuilder();
    private final LineBuilder _headerLineBuilder = new LineBuilder();
    private final ConsoleReader _reader;
    private int _height;
    private int _width;
    private int _line;
    private volatile int _posX = 0;
    private volatile int _posY = 0;
    private int _leftRightMoveSize;
    private int _headerLine;

    public Canvas(ConsoleReader reader) {
      _reader = reader;
    }

    public void resetHeader() {
      _headerLine = 0;
      _headerScreenBuilder.setLength(0);
    }

    public void reset() {
      _line = 0;
      _screenBuilder.setLength(0);
      Terminal terminal = _reader.getTerminal();
      _height = terminal.getHeight() - 3;
      _width = terminal.getWidth() - 2;
      _leftRightMoveSize = _width / 4;
    }

    public void endLine() {
      _lineBuilder.close();
      int pos = _line - _posY;
      if (pos >= 0 && pos < (_height - _headerLine)) {
        int end = _posX + _width;
        int s = _posX;
        int e = Math.min(_lineBuilder.visibleLength(), end);
        if (e > s) {
          _screenBuilder.append(_lineBuilder.substring(s, e));
        }
        _screenBuilder.append('\n');
      }
      _line++;
      _lineBuilder.reset();
    }

    public void endHeader() {
      _headerLineBuilder.close();
      int end = _posX + _width;
      int s = _posX;
      int e = Math.min(_headerLineBuilder.visibleLength(), end);
      if (e > s) {
        _headerScreenBuilder.append(_headerLineBuilder.substring(s, e));
      }
      _headerScreenBuilder.append('\n');
      _headerLineBuilder.reset();
      _headerLine++;
    }

    public void startLine() {

    }

    public void startHeader() {

    }

    public void appendHeader(char c) {
      _headerLineBuilder.append(c);
    }

    public void appendHeader(String s) {
      _headerLineBuilder.append(s);
    }

    public void append(char c) {
      _lineBuilder.append(c);
    }

    public void append(String s) {
      _lineBuilder.append(s);
    }

    public void write() throws IOException {
      _reader.clearScreen();
      Writer writer = _reader.getOutput();
      writer.write(_headerScreenBuilder.toString());
      writer.write(_screenBuilder.toString());
      writer.flush();
    }

    public void moveLeft() {
      if (_posX > 0) {
        _posX -= _leftRightMoveSize;
      }
    }

    public void moveRight() {
      _posX += _leftRightMoveSize;
    }

    public void moveUp() {
      if (_posY > 0) {
        _posY--;
      }
    }

    public void moveDown() {
      _posY++;
    }

    public void pageUp() {
      if (_posY < _height) {
        _posY = 0;
      } else {
        _posY -= _height;
      }
    }

    public void pageDown() {
      _posY += _height;
    }

  }

  public void setStopReadingInput(boolean b) {
    _stopReadingInput.set(true);
  }
}
TOP

Related Classes of org.apache.blur.shell.TableDisplay$LineBuilder

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.