/** * This file is part of git-as-svn. It is subject to the license terms * in the LICENSE file found in the top-level directory of this distribution * and at http://www.gnu.org/licenses/gpl-2.0.html. No part of git-as-svn, * including this file, may be copied, modified, propagated, or distributed * except according to the terms contained in the LICENSE file. */ package svnserver.parser.token; import org.jetbrains.annotations.NotNull; import svnserver.StringHelper; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.util.Arrays; /** * Ключевое слово. * <p> * Бинарная строка или текст известной длины. * * @author Artem V. Navrotskiy <bozaro@users.noreply.github.com> */ public final class StringToken implements TextToken { @NotNull private final byte[] data; public StringToken(@NotNull byte[] data) { this.data = data; } public StringToken(@NotNull String text) { this.data = text.getBytes(StandardCharsets.UTF_8); } @Override @NotNull public String getText() { return new String(data, StandardCharsets.UTF_8); } @NotNull public byte[] getData() { return data; } @Override public void write(@NotNull OutputStream stream) throws IOException { write(stream, data, 0, data.length); } public static void write(@NotNull OutputStream stream, @NotNull byte[] data, int offset, int length) throws IOException { stream.write(Long.toString(length, 10).getBytes(StandardCharsets.ISO_8859_1)); stream.write(':'); stream.write(data, offset, length); stream.write(' '); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final StringToken other = (StringToken) o; return Arrays.equals(data, other.data); } @Override public int hashCode() { return Arrays.hashCode(data); } @Override public String toString() { final String value; if (isUtf(data)) { value = '\"' + new String(data, StandardCharsets.UTF_8) + '\"'; } else { value = "0x" + StringHelper.toHex(data); } return "String{" + value + '}'; } @SuppressWarnings("MagicNumber") private static boolean isUtf(byte[] data) { int i = 0; while (i < data.length) { int continuationBytes; //noinspection IfStatementWithTooManyBranches if (data[i] <= 0x7F) continuationBytes = 0; else if (data[i] >= 0xC0 /*11000000*/ && data[i] <= 0xDF /*11011111*/) continuationBytes = 1; else if (data[i] >= 0xE0 /*11100000*/ && data[i] <= 0xEF /*11101111*/) continuationBytes = 2; else if (data[i] >= 0xF0 /*11110000*/ && data[i] <= 0xF4 /* Cause of RFC 3629 */) continuationBytes = 3; else return false; i += 1; while (i < data.length && continuationBytes > 0 && data[i] >= 0x80 && data[i] <= 0xBF) { i += 1; continuationBytes -= 1; } if (continuationBytes != 0) return false; } return true; } }