blob: eab4de3504000945be8543dcdd7d24c523da5b34 [file] [log] [blame]
/*
* Copyright (c) 2002-2016, the original author or authors.
*
* This software is distributable under the BSD license. See the terms of the
* BSD license in the documentation provided with this software.
*
* https://opensource.org/licenses/BSD-3-Clause
*/
package jdk.internal.org.jline.utils;
import java.security.InvalidParameterException;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Attributed string.
* Instances of this class are immutables.
* Substrings are created without any memory copy.
*
* @author <a href="mailto:gnodet@gmail.com">Guillaume Nodet</a>
*/
public class AttributedString extends AttributedCharSequence {
final char[] buffer;
final int[] style;
final int start;
final int end;
public static final AttributedString EMPTY = new AttributedString("");
public static final AttributedString NEWLINE = new AttributedString("\n");
public AttributedString(CharSequence str) {
this(str, 0, str.length(), null);
}
public AttributedString(CharSequence str, int start, int end) {
this(str, start, end, null);
}
public AttributedString(CharSequence str, AttributedStyle s) {
this(str, 0, str.length(), s);
}
public AttributedString(CharSequence str, int start, int end, AttributedStyle s) {
if (end < start) {
throw new InvalidParameterException();
}
if (str instanceof AttributedString) {
AttributedString as = (AttributedString) str;
this.buffer = as.buffer;
if (s != null) {
this.style = as.style.clone();
for (int i = 0; i < style.length; i++) {
this.style[i] = (this.style[i] & ~s.getMask()) | s.getStyle();
}
} else {
this.style = as.style;
}
this.start = as.start + start;
this.end = as.start + end;
} else if (str instanceof AttributedStringBuilder) {
AttributedStringBuilder asb = (AttributedStringBuilder) str;
AttributedString as = asb.subSequence(start, end);
this.buffer = as.buffer;
this.style = as.style;
if (s != null) {
for (int i = 0; i < style.length; i++) {
this.style[i] = (this.style[i] & ~s.getMask()) | s.getStyle();
}
}
this.start = as.start;
this.end = as.end;
} else {
int l = end - start;
buffer = new char[l];
for (int i = 0; i < l; i++) {
buffer[i] = str.charAt(start + i);
}
style = new int[l];
if (s != null) {
Arrays.fill(style, s.getStyle());
}
this.start = 0;
this.end = l;
}
}
AttributedString(char[] buffer, int[] style, int start, int end) {
this.buffer = buffer;
this.style = style;
this.start = start;
this.end = end;
}
public static AttributedString fromAnsi(String ansi) {
return fromAnsi(ansi, 0);
}
public static AttributedString fromAnsi(String ansi, int tabs) {
return fromAnsi(ansi, Arrays.asList(tabs));
}
public static AttributedString fromAnsi(String ansi, List<Integer> tabs) {
if (ansi == null) {
return null;
}
return new AttributedStringBuilder(ansi.length())
.tabs(tabs)
.ansiAppend(ansi)
.toAttributedString();
}
public static String stripAnsi(String ansi) {
if (ansi == null) {
return null;
}
return new AttributedStringBuilder(ansi.length())
.ansiAppend(ansi)
.toString();
}
@Override
protected char[] buffer() {
return buffer;
}
@Override
protected int offset() {
return start;
}
@Override
public int length() {
return end - start;
}
@Override
public AttributedStyle styleAt(int index) {
return new AttributedStyle(style[start + index], style[start + index]);
}
@Override
int styleCodeAt(int index) {
return style[start + index];
}
@Override
public AttributedString subSequence(int start, int end) {
return new AttributedString(this, start, end);
}
public AttributedString styleMatches(Pattern pattern, AttributedStyle style) {
Matcher matcher = pattern.matcher(this);
boolean result = matcher.find();
if (result) {
int[] newstyle = this.style.clone();
do {
for (int i = matcher.start(); i < matcher.end(); i++) {
newstyle[this.start + i] = (newstyle[this.start + i] & ~style.getMask()) | style.getStyle();
}
result = matcher.find();
} while (result);
return new AttributedString(buffer, newstyle, start , end);
}
return this;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AttributedString that = (AttributedString) o;
return end - start == that.end - that.start
&& arrEq(buffer, that.buffer, start, that.start, end - start)
&& arrEq(style, that.style, start, that.start, end - start);
}
private boolean arrEq(char[] a1, char[] a2, int s1, int s2, int l) {
for (int i = 0; i < l; i++) {
if (a1[s1+i] != a2[s2+i]) {
return false;
}
}
return true;
}
private boolean arrEq(int[] a1, int[] a2, int s1, int s2, int l) {
for (int i = 0; i < l; i++) {
if (a1[s1+i] != a2[s2+i]) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
int result = Arrays.hashCode(buffer);
result = 31 * result + Arrays.hashCode(style);
result = 31 * result + start;
result = 31 * result + end;
return result;
}
public static AttributedString join(AttributedString delimiter, AttributedString... elements) {
Objects.requireNonNull(delimiter);
Objects.requireNonNull(elements);
return join(delimiter, Arrays.asList(elements));
}
public static AttributedString join(AttributedString delimiter, Iterable<AttributedString> elements) {
Objects.requireNonNull(elements);
AttributedStringBuilder sb = new AttributedStringBuilder();
int i = 0;
for (AttributedString str : elements) {
if (i++ > 0 && delimiter != null) {
sb.append(delimiter);
}
sb.append(str);
}
return sb.toAttributedString();
}
}