| /** |
| * 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. |
| */ |
| |
| /* |
| * "Buffer.java" |
| * Buffer.java 1.8 01/07/30 |
| */ |
| package org.netbeans.lib.terminalemulator; |
| |
| import java.util.Vector; |
| |
| /** |
| * The Buffer used by Term is _not_ related to javax.swing.text.Document. |
| * <p> |
| * The Swing Document is Element based while terms is Line based. |
| * <br> |
| * The Swing Document uses offsets for coordinates, while term uses cartesian |
| * BCoords. |
| * <p> |
| */ |
| class Buffer { |
| |
| /* |
| * For some odd reason Vector.removeRange is protected, so |
| * we have to do this to gain access to it. |
| */ |
| private static class OurVector<T> extends Vector<T> { |
| |
| @Override |
| public void removeRange(int fromIndex, int toIndex) { |
| super.removeRange(fromIndex, toIndex); |
| } |
| } |
| private final OurVector<Line> lines = new OurVector<>(); // buffer |
| |
| // How is this different from lines.length? |
| private int nlines; // number of lines in buffer |
| |
| private int visible_cols; // number of columns visible in view |
| private int extra_cols; // columns needed to support lines longer |
| // than visible_cols. Only grows. |
| |
| /** |
| * @return nlines |
| */ |
| int nlines() { |
| return nlines; |
| } |
| |
| public int visibleCols() { |
| return visible_cols; |
| } |
| |
| public int totalCols() { |
| return visible_cols + extra_cols; |
| } |
| |
| public Buffer(int visible_cols) { |
| this.visible_cols = visible_cols; |
| } |
| |
| public void setVisibleCols(int visible_cols) { |
| int delta = visible_cols - this.visible_cols; |
| this.visible_cols = visible_cols; |
| extra_cols -= delta; |
| if (extra_cols < 0) { |
| extra_cols = 0; |
| } |
| } |
| |
| /* |
| * Keep track of the largest column # to help set the extent of |
| * the horizontal scrollbar. |
| */ |
| public void noteColumn(int col) { |
| int new_extra = col - visible_cols; |
| if (new_extra > extra_cols) { |
| extra_cols = new_extra; |
| // LATER hrange_listener.adjustHRange(extra_cols); |
| } |
| } |
| |
| /* DEBUG |
| public static volatile boolean lock = false; |
| |
| private void ck_lock() { |
| if (lock) { |
| System.out.println("Buffer ck_lock fail"); // NOI18N |
| printStats(); |
| Thread.dumpStack(); |
| } |
| } |
| */ |
| Line lineAt(int brow) { |
| try { |
| return lines.elementAt(brow); |
| } catch (ArrayIndexOutOfBoundsException x) { |
| //XXX swallowing this exception caused issue 40129. |
| //I've put in a null-check on the return value in sel.paint() |
| //as a hotfix. Should find out why bad values are being passed |
| //here. Ivan? |
| |
| /* DEBUG |
| System.out.println("Buffer.lineAt(" +brow+ ") -> null\n");// NOI18N |
| Thread.dumpStack(); |
| */ |
| return null; |
| } |
| } |
| |
| Line bottom() { |
| return lineAt(nlines); |
| } |
| |
| public Line appendLine() { |
| // DEBUG ck_lock(); |
| Line l = new Line(); |
| lines.add(l); |
| nlines++; |
| return l; |
| } |
| |
| public Line addLineAt(int row) { |
| // DEBUG ck_lock(); |
| Line l = new Line(); |
| lines.add(row, l); |
| nlines++; |
| return l; |
| } |
| |
| /** |
| * Remove 'n' lines starting at 'row'. |
| * Return the number of characters deleted as a result. |
| */ |
| public int removeLinesAt(int row, int n) { |
| // DEBUG ck_lock(); |
| int nchars = 0; |
| for (int r = row; r < row + n; r++) { |
| nchars += lineAt(r).length() + 1; |
| } |
| |
| lines.removeRange(row, row + n); |
| nlines -= n; |
| |
| return nchars; |
| } |
| |
| public void removeLineAt(int row) { |
| // DEBUG ck_lock(); |
| lines.remove(row); |
| nlines--; |
| } |
| |
| public Line moveLineFromTo(int from, int to) { |
| // DEBUG ck_lock(); |
| Line l = lines.remove(from); |
| lines.add(to, l); |
| return l; |
| } |
| |
| /** |
| * Visit the physical lines from 'begin', through 'end'. |
| * <p> |
| * If 'newlines' is set, the passed 'ecol' is set to the actual |
| * number of columns in the view to signify that the newline is included. |
| * This way of doing it helps with rendering of a whole-line selection. |
| * Also Line knows about this and will tack on a "\n" when Line.text() |
| * is asked for. |
| */ |
| void visitLines(BCoord begin, BCoord end, boolean newlines, |
| LineVisitor visitor) { |
| |
| // In the general case a range is made up of three |
| // rectangles. The partial line at top, the partial line |
| // at the bottom and the middle range of fully selected lines. |
| |
| Line l; |
| if (begin.row == end.row) { |
| // range is on one line |
| l = lineAt(begin.row); |
| visitor.visit(l, begin.row, begin.col, end.col); |
| |
| } else { |
| boolean cont; |
| |
| // range spans multiple lines |
| l = lineAt(begin.row); |
| if (newlines && !l.isWrapped()) { |
| cont = visitor.visit(l, begin.row, begin.col, totalCols()); |
| } else { |
| cont = visitor.visit(l, begin.row, begin.col, l.length() - 1); |
| } |
| if (!cont) { |
| return; |
| } |
| |
| for (int r = begin.row + 1; r < end.row; r++) { |
| l = lineAt(r); |
| if (newlines && !l.isWrapped()) { |
| cont = visitor.visit(l, r, 0, totalCols()); |
| } else { |
| cont = visitor.visit(l, r, 0, l.length() - 1); |
| } |
| if (!cont) { |
| return; |
| } |
| } |
| |
| l = lineAt(end.row); |
| cont = visitor.visit(l, end.row, 0, end.col); |
| if (!cont) { |
| return; |
| } |
| } |
| } |
| |
| /* |
| * Like visitLines() except in reverse. |
| * <p> |
| * Starts at 'end' and goes to 'begin'. |
| */ |
| void reverseVisitLines(BCoord begin, BCoord end, boolean newlines, |
| LineVisitor visitor) { |
| |
| // very similar to visitLines |
| |
| Line l; |
| if (begin.row == end.row) { |
| // range is on one line |
| l = lineAt(begin.row); |
| visitor.visit(l, begin.row, begin.col, end.col); |
| |
| } else { |
| boolean cont; |
| |
| // range spans multiple lines |
| l = lineAt(end.row); |
| cont = visitor.visit(l, end.row, 0, end.col); |
| if (!cont) { |
| return; |
| } |
| |
| for (int r = end.row - 1; r > begin.row; r--) { |
| l = lineAt(r); |
| if (newlines && !l.isWrapped()) { |
| cont = visitor.visit(l, r, 0, totalCols()); |
| } else { |
| cont = visitor.visit(l, r, 0, l.length() - 1); |
| } |
| if (!cont) { |
| return; |
| } |
| } |
| |
| l = lineAt(begin.row); |
| if (newlines && !l.isWrapped()) { |
| cont = visitor.visit(l, begin.row, begin.col, totalCols()); |
| } else { |
| cont = visitor.visit(l, begin.row, begin.col, l.length() - 1); |
| } |
| if (!cont) { |
| return; |
| } |
| } |
| } |
| |
| public BExtent find_line(BCoord coord) { |
| WordDelineator newLine = WordDelineator.createNewlineDelineator(); |
| return find_word(newLine, coord); |
| } |
| |
| public BExtent find_word(WordDelineator word_delineator, BCoord coord) { |
| /* |
| * Find the boundaries of a "word" at 'coord'. |
| */ |
| |
| int row = coord.row; |
| Line startLine = lineAt(row); |
| |
| if (coord.col >= startLine.length()) { |
| return new BExtent(coord, coord); |
| } |
| |
| int lx = 0; |
| int rx = 0; |
| int beginRow = row; |
| int endRow = row; |
| StringBuffer lineBuffer; |
| |
| // Try to use lineVisitor |
| while (beginRow >= 0) { |
| Line line = lineAt(beginRow); |
| lineBuffer = line.stringBuffer(); |
| int searchCol = (lx == -1) ? line.length() - 1 : coord.col; |
| lx = word_delineator.findLeft(lineBuffer, searchCol, true); |
| |
| if (lx != -1) { |
| break; |
| } else { |
| if (beginRow == 0) { |
| lx = 0; |
| } else if (!lineAt(beginRow - 1).isWrapped()) { |
| lx = 0; |
| break; |
| } |
| } |
| beginRow--; |
| } |
| while (endRow < nlines) { |
| Line line = lineAt(endRow); |
| lineBuffer = line.stringBuffer(); |
| int searchCol = (rx == -1) ? 0 : coord.col; |
| rx = word_delineator.findRight(lineBuffer, searchCol, true); |
| |
| if (rx != -1) { |
| break; |
| } else { |
| if (endRow == nlines - 1) { |
| rx = line.length() - 1; |
| } |
| if (!line.isWrapped()) { |
| rx = line.length() - 1; |
| break; |
| } |
| } |
| endRow++; |
| } |
| |
| return new BExtent(new BCoord(beginRow, lx), |
| new BCoord(endRow, rx)); |
| } |
| |
| /** |
| * Back up the coordinate by one character and return new BCoord |
| * <p> |
| * Travels back over line boundaries |
| * <br> |
| * Returns null if 'c' is the first character of the buffer. |
| */ |
| public BCoord backup(BCoord c) { |
| if (c.col > 0) { |
| return new BCoord(c.row, c.col - 1); // back one in line |
| } |
| // Cursor is at beginning of line. |
| // Need to find the end of previous line, but it might empty, |
| // so we go one line back etc |
| for (int prevrow = c.row - 1; prevrow >= 0; prevrow--) { |
| Line l = lineAt(prevrow); |
| if (l.length() != 0) { |
| return new BCoord(prevrow, l.length() - 1); |
| } |
| } |
| |
| // prevrow == -1, at beginning of file; nowhere to back to |
| return null; |
| } |
| |
| /* |
| * Advance the coordinate by one character and return a new coord. |
| * <p> |
| * Wraps around line boundaries. |
| * <br> |
| * Returns null if 'c' is at the last character of the buffer. |
| */ |
| @SuppressWarnings("ValueOfIncrementOrDecrementUsed") |
| public BCoord advance(BCoord c) { |
| int row = c.row; |
| int col = c.col; |
| |
| col++; |
| Line l = lineAt(row); |
| if (col < l.length()) { |
| return new BCoord(row, col); |
| } |
| |
| // Need to wrap, but the next line might be empty ... so we |
| // keep going til either we find a non-empty line or the end |
| // of the buffer. |
| while (++row < nlines) { |
| l = lineAt(row); |
| if (l.length() != 0) { |
| return new BCoord(row, 0); |
| } |
| } |
| return null; |
| } |
| |
| private int utilization(int nchars, int ncapacity) { |
| float u = ((float) nchars) / ((float) ncapacity); |
| float p = u * 100.0f; // convert to percentage |
| return (int) p; |
| } |
| /* |
| * Print interesting statistics and facts about this Term |
| */ |
| |
| public void printStats(boolean indented) { |
| int chars = 0; |
| int charcapacity = 0; |
| |
| int nalines = 0; // lines with attributes |
| int attrs = 0; |
| int attrcapacity = 0; |
| |
| for (int lx = 0; lx < nlines; lx++) { |
| Line l = lineAt(lx); |
| chars += l.length(); |
| charcapacity += l.capacity(); |
| if (l.hasAttributes()) { |
| nalines++; |
| attrs += l.length(); |
| attrcapacity += l.capacity(); |
| } |
| } |
| |
| Term.indent(indented); |
| System.out.println("Buffer:" + // NOI18N |
| " nlines " + nlines); // NOI18N |
| |
| Term.indent(indented); |
| System.out.println(" " + // NOI18N |
| " chars " + chars + // NOI18N |
| " charcapacity " + charcapacity + // NOI18N |
| " utilzn %" + utilization(chars, charcapacity)); // NOI18N |
| |
| Term.indent(indented); |
| System.out.println(" " + // NOI18N |
| " attrs " + attrs + // NOI18N |
| " attrcapacity " + attrcapacity + // NOI18N |
| " utilzn %" + utilization(attrs, attrcapacity)); // NOI18N |
| |
| // estimate actual byte consumption |
| final int unitObjectSz = 8; |
| final int unitReferenceSz = 4; |
| |
| final int unitBooleanSz = 4; |
| final int unitCharSz = 2; |
| final int unitIntSz = 4; |
| |
| final int unitCharArraySz = 12; |
| final int unitIntArraySz = 12; |
| |
| long bytes = 0; |
| long unitLineSz = unitObjectSz + |
| unitBooleanSz + // about_to_wrap |
| unitReferenceSz + // attr |
| unitIntSz + // backgroundColor |
| unitReferenceSz + // buf |
| unitIntSz + // capacity |
| unitIntSz + // glyphId |
| unitIntSz + // length |
| unitBooleanSz // wrapped |
| ; |
| bytes += unitLineSz * nlines; |
| |
| bytes += unitCharArraySz * nlines; |
| bytes += unitCharSz * charcapacity; |
| |
| bytes += unitIntArraySz * nalines; |
| bytes += unitIntSz * attrcapacity; |
| |
| Term.indent(indented); |
| System.out.println(" " + // NOI18N |
| " bytes " + bytes / 1024 + "K"); // NOI18N |
| |
| } |
| } |