aboutsummaryrefslogtreecommitdiffstats
path: root/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java
diff options
context:
space:
mode:
Diffstat (limited to 'node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java175
1 files changed, 175 insertions, 0 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java
new file mode 100644
index 00000000000..8d7c7d8269a
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/task/util/editor/TextBuffer.java
@@ -0,0 +1,175 @@
+// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+package com.yahoo.vespa.hosted.node.admin.task.util.editor;
+
+import java.util.Optional;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * @author hakon
+ */
+interface TextBuffer {
+ // INTERFACE TO IMPLEMENT BY CONCRETE CLASS
+
+ /** Get the version of the buffer - edits increment the version. */
+ Version getVersion();
+
+ /** Return the text as a single String (likely) with embedded newlines. */
+ String getString();
+
+ /** Return the maximum line index (the minimum line index is 0). */
+ int getMaxLineIndex();
+
+ /** @param lineIndex must be in in {@code [0, getMaxLineIndex()]} */
+ String getLine(int lineIndex);
+
+ /** Insert the possibly multi-line text at position and return the end position. */
+ Position write(Position position, String text);
+
+ /** Delete everything. */
+ void clear();
+
+ /** Delete range. */
+ void delete(Position start, Position end);
+
+ // DERIVED IMPLEMENTATION
+
+ /**
+ * Return the Position closest to {@code position} which is in the range
+ * {@code [getStartOfText(), getEndOfText()]}.
+ */
+ default Position getValidPositionClosestTo(Position position) {
+ if (position.isBefore(getStartOfText())) {
+ return getStartOfText();
+ } else if (position.isAfter(getEndOfText())) {
+ return getEndOfText();
+ } else {
+ return position;
+ }
+ }
+
+ default String getLine(Position position) { return getLine(position.lineIndex()); }
+
+ default String getLinePrefix(Position position) {
+ return getLine(position.lineIndex()).substring(0, position.columnIndex());
+ }
+
+ default String getLineSuffix(Position position) {
+ return getLine(position.lineIndex()).substring(position.columnIndex());
+ }
+
+ default String getSubstring(Position start, Position end) {
+ if (start.lineIndex() < end.lineIndex()) {
+ StringBuilder builder = new StringBuilder(getLineSuffix(start));
+ for (int i = start.lineIndex() + 1; i < end.lineIndex(); ++i) {
+ builder.append('\n');
+ builder.append(getLine(i));
+ }
+ return builder.append('\n').append(getLinePrefix(end)).toString();
+ } else if (start.lineIndex() == end.lineIndex() && start.columnIndex() <= end.columnIndex()) {
+ return getLine(start).substring(start.columnIndex(), end.columnIndex());
+ }
+
+ throw new IllegalArgumentException(
+ "Bad range [" + start.coordinateString() + "," + end.coordinateString() + ">");
+ }
+
+ default Position getStartOfText() { return Position.start(); } // aka (0,0)
+
+ default Position getEndOfText() {
+ int maxLineIndex = getMaxLineIndex();
+ return new Position(maxLineIndex, getLine(maxLineIndex).length());
+ }
+
+ default Position getStartOfLine(Position position) {
+ return new Position(position.lineIndex(), 0);
+ }
+
+ default Position getEndOfLine(Position position) {
+ return new Position(position.lineIndex(), getLine(position).length());
+ }
+
+ default Position getStartOfNextLine(Position position) {
+ if (position.lineIndex() < getMaxLineIndex()) {
+ return new Position(position.lineIndex() + 1, 0);
+ } else {
+ return getEndOfText();
+ }
+ }
+
+ default Position getStartOfPreviousLine(Position position) {
+ int lineIndex = position.lineIndex();
+ if (lineIndex > 0) {
+ return new Position(lineIndex - 1, 0);
+ } else {
+ return getStartOfText();
+ }
+ }
+
+ default Position forward(Position position, int length) {
+ int lineIndex = position.lineIndex();
+ int columnIndex = position.columnIndex();
+
+ int offsetLeft = length;
+ do {
+ String line = getLine(lineIndex);
+ int columnIndexWithInfiniteLine = columnIndex + offsetLeft;
+ if (columnIndexWithInfiniteLine <= line.length()) {
+ return new Position(lineIndex, columnIndexWithInfiniteLine);
+ } else if (lineIndex >= getMaxLineIndex()) {
+ // End of text
+ return new Position(lineIndex, line.length());
+ }
+
+ offsetLeft -= line.length() - columnIndex;
+
+ // advance past newline
+ --offsetLeft;
+ ++lineIndex;
+ columnIndex = 0;
+
+ // At this point: offsetLeft is guaranteed to be >= 0, and lineIndex <= max line index
+ } while (true);
+ }
+
+ default Position backward(Position position, int length) {
+ int lineIndex = position.lineIndex();
+ int columnIndex = position.columnIndex();
+
+ int offsetLeft = length;
+ do {
+ int columnIndexWithInfiniteLine = columnIndex - offsetLeft;
+ if (columnIndexWithInfiniteLine >= 0) {
+ return new Position(lineIndex, columnIndexWithInfiniteLine);
+ } else if (lineIndex <= 0) {
+ // Start of text
+ return new Position(0, 0);
+ }
+
+ offsetLeft -= columnIndex;
+
+ // advance past newline
+ --offsetLeft;
+ --lineIndex;
+ columnIndex = getLine(lineIndex).length();
+
+ // At this point: offsetLeft is guaranteed to be <= 0, and lineIndex >= 0
+ } while (true);
+ }
+
+ default Optional<Match> findForward(Position startPosition, Pattern pattern) {
+ for (Position position = startPosition;; position = getStartOfNextLine(position)) {
+ String line = getLine(position);
+ Matcher matcher = pattern.matcher(line);
+ if (matcher.find(position.columnIndex())) {
+ return Optional.of(new Match(position.lineIndex(), line, matcher));
+ }
+
+ if (position.lineIndex() == getMaxLineIndex()) {
+ // search failed - no lines matched
+ return Optional.empty();
+ }
+ }
+ }
+}