Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2016-2020 chronicle.software
*
* https://chronicle.software
*
* Licensed 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 net.openhft.chronicle.wire;
import net.openhft.chronicle.bytes.Byteable;
import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.bytes.BytesStore;
import net.openhft.chronicle.bytes.BytesUtil;
import net.openhft.chronicle.bytes.ref.TextBooleanReference;
import net.openhft.chronicle.bytes.ref.TextIntReference;
import net.openhft.chronicle.bytes.ref.TextLongArrayReference;
import net.openhft.chronicle.bytes.ref.TextLongReference;
import net.openhft.chronicle.bytes.render.GeneralDecimaliser;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.Maths;
import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.IOTools;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.core.pool.ClassLookup;
import net.openhft.chronicle.core.values.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Externalizable;
import java.io.IOException;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.function.BiConsumer;
import static net.openhft.chronicle.bytes.BytesStore.empty;
/**
* Provides functionality for writing data in a YAML-based wire format.
* This class encapsulates methods and attributes to handle data serialization into YAML format.
*
* @param The type that extends YamlWireOut
*/
@SuppressWarnings({"rawtypes", "unchecked", "this-escape", "deprecation"})
public abstract class YamlWireOut> extends AbstractWire {
@Deprecated(/* to remove in x.28 */)
private static final boolean APPEND_0 = Jvm.getBoolean("bytes.append.0", true);
public static final BytesStore, ?> TYPE = BytesStore.from("!type ");
static final String NULL = "!null \"\"";
static final BitSet STARTS_QUOTE_CHARS = new BitSet();
static final BitSet QUOTE_CHARS = new BitSet();
static final BytesStore, ?> COMMA_SPACE = BytesStore.from(", ");
static final BytesStore, ?> COMMA_NEW_LINE = BytesStore.from(",\n");
static final BytesStore, ?> NEW_LINE = BytesStore.from("\n");
static final BytesStore, ?> EMPTY_AFTER_COMMENT = BytesStore.wrap(new byte[0]); // not the same as EMPTY, so we can check this value.
static final BytesStore, ?> EMPTY = BytesStore.from("");
static final BytesStore, ?> SPACE = BytesStore.from(" ");
static final BytesStore, ?> END_FIELD = NEW_LINE;
static final char[] HEXADECIMAL = "0123456789ABCDEF".toCharArray();
// Static initializer block to configure quote characters for the YAML writer
static {
IOTools.unmonitor(TYPE);
for (char ch : "?%*&@`0123456789+- ',#:{}[]|>!\\".toCharArray())
STARTS_QUOTE_CHARS.set(ch);
for (char ch : "?,#:{}[]|>\\^".toCharArray())
QUOTE_CHARS.set(ch);
// make sure it has loaded.
WireInternal.INTERNER.valueCount();
}
protected final YamlValueOut valueOut = createValueOut();
protected final StringBuilder sb = new StringBuilder();
private boolean addTimeStamps = false;
private boolean trimFirstCurly = true;
/**
* Constructs a new instance of YamlWireOut with specified bytes and 8-bit flag.
*
* @param bytes The bytes buffer for the wire format.
* @param use8bit Boolean flag indicating whether to use 8-bit values.
*/
protected YamlWireOut(@NotNull Bytes bytes, boolean use8bit) {
super(bytes, use8bit);
bytes.decimaliser(GeneralDecimaliser.GENERAL);
}
/**
* Checks if timestamps should be added during serialization.
*
* @return True if timestamps should be added, otherwise false.
*/
public boolean addTimeStamps() {
return addTimeStamps;
}
/**
* Configures whether to add timestamps during serialization.
* This method follows the builder pattern allowing chained method calls.
*
* @param addTimeStamps Boolean indicating whether to add timestamps.
* @return The current instance of YamlWireOut.
*/
public T addTimeStamps(boolean addTimeStamps) {
this.addTimeStamps = addTimeStamps;
return (T) this;
}
/**
* Creates and returns a new instance of {@link YamlValueOut}.
*
* @return A new YamlValueOut instance.
*/
@NotNull
protected YamlValueOut createValueOut() {
return new YamlValueOut();
}
/**
* Acquires and clears the internal StringBuilder {@code sb} for use.
* The method ensures the StringBuilder's count is reset to 0 before returning.
*
* @return The internal StringBuilder after it has been cleared.
*/
@NotNull
protected StringBuilder acquireStringBuilder() {
sb.setLength(0);
return sb;
}
@NotNull
@Override
public Bytes> bytes() {
return bytes;
}
@NotNull
@Override
public ValueOut write() {
return valueOut.write();
}
@NotNull
@Override
public ValueOut write(@NotNull WireKey key) {
return valueOut.write(key);
}
@NotNull
@Override
public ValueOut write(@NotNull CharSequence name) {
return valueOut.write(name);
}
@Override
public ValueOut writeEvent(Class> expectedType, Object eventKey) throws InvalidMarshallableException {
if (eventKey instanceof WireKey)
return writeEventName((WireKey) eventKey);
if (eventKey instanceof CharSequence)
return writeEventName((CharSequence) eventKey);
if (expectedType != null && expectedType.isInstance(eventKey)) {
if (eventKey instanceof Enum)
return writeEventName(((Enum) eventKey).name());
if (eventKey instanceof DynamicEnum)
return writeEventName(((DynamicEnum) eventKey).name());
if (eventKey instanceof Boolean)
return writeEventName(eventKey.toString());
}
boolean wasLeft = valueOut.swapLeaf(true);
try {
return valueOut.write(expectedType, eventKey);
} finally {
valueOut.swapLeaf(wasLeft);
}
}
@NotNull
@Override
public T dropDefault(boolean dropDefault) {
valueOut.dropDefault = dropDefault;
return (T) this;
}
@NotNull
@Override
public ValueOut getValueOut() {
return valueOut;
}
@NotNull
@Override
public T writeComment(@NotNull CharSequence s) {
valueOut.writeComment(s);
return (T) this;
}
@NotNull
@Override
public T addPadding(int paddingToAdd) {
for (int i = 0; i < paddingToAdd; i++)
bytes.writeUnsignedByte((bytes.writePosition() & 63) == 0 ? '\n' : ' ');
return (T) this;
}
/**
* Escapes the given CharSequence {@code s} based on the requirements of the YAML format.
* If the sequence requires quotes, it will be enclosed with the appropriate quote character;
* otherwise, the sequence will be escaped without quotes.
*
* @param s The CharSequence to be escaped.
*/
void escape(@NotNull CharSequence s) {
@NotNull Quotes quotes = needsQuotes(s);
if (quotes == Quotes.NONE) {
escape0(s, quotes);
return;
}
bytes.writeUnsignedByte(quotes.q);
escape0(s, quotes);
bytes.writeUnsignedByte(quotes.q);
}
// https://yaml.org/spec/1.2.2/#escaped-characters
/**
* Helper method to escape special characters in the given CharSequence {@code s} based on the requirements of the YAML format.
* The method handles the specific escaping requirements for various control and special characters.
*
* @param s The CharSequence to be escaped.
* @param quotes The type of quotes used to determine how certain characters are escaped.
*/
protected void escape0(@NotNull CharSequence s, @NotNull Quotes quotes) {
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
switch (ch) {
case '\0':
bytes.append("\\0"); // Null character
break;
case 7:
bytes.append("\\a"); // Bell (alert)
break;
case '\b':
bytes.append("\\b"); // Backspace
break;
case '\t':
bytes.append("\\t"); // Horizontal tab
break;
case '\n':
bytes.append("\\n"); // Newline
break;
case 0xB:
bytes.append("\\v"); // Vertical tab
break;
case '\f':
bytes.append("\\f"); // Formfeed
break;
case '\r':
bytes.append("\\r"); // Carriage return
break;
case 0x1B:
bytes.append("\\e"); // Escape
break;
case '"':
// Handling double quotes
if (ch == quotes.q) {
bytes.writeUnsignedByte('\\').writeUnsignedByte(ch);
} else {
bytes.writeUnsignedByte(ch);
}
break;
case '\'':
// Handling single quotes
if (ch == quotes.q) {
bytes.writeUnsignedByte('\\').writeUnsignedByte(ch);
} else {
bytes.writeUnsignedByte(ch);
}
break;
case '\\':
bytes.writeUnsignedByte('\\').writeUnsignedByte(ch); // Escape backslash itself
break;
case 0x85:
bytes.appendUtf8("\\N"); // Next line
break;
case 0xA0:
bytes.appendUtf8("\\_"); // Non-breaking space
break;
case 0x2028:
bytes.appendUtf8("\\L"); // Line separator
break;
case 0x2029:
bytes.appendUtf8("\\P"); // Paragraph separator
break;
default:
// Handling characters outside the ASCII range and other special characters
if (ch > 255)
appendU4(ch);
else if (ch < ' ' || ch > 127)
appendX2(ch);
else
bytes.appendUtf8(ch);
break;
}
}
}
/**
* Appends a 2-character hexadecimal representation of the given character {@code ch} to the output bytes.
* This is used for character escaping.
*
* @param ch The character to be converted to hexadecimal.
*/
private void appendX2(char ch) {
bytes.append('\\');
bytes.append('x');
bytes.append(HEXADECIMAL[(ch >> 4) & 0xF]);
bytes.append(HEXADECIMAL[ch & 0xF]);
}
/**
* Appends a 4-character hexadecimal Unicode representation of the given character {@code ch} to the output bytes.
* This is used for character escaping.
*
* @param ch The character to be converted to hexadecimal Unicode representation.
*/
protected void appendU4(char ch) {
bytes.append('\\');
bytes.append('u');
bytes.append(HEXADECIMAL[ch >> 12]);
bytes.append(HEXADECIMAL[(ch >> 8) & 0xF]);
bytes.append(HEXADECIMAL[(ch >> 4) & 0xF]);
bytes.append(HEXADECIMAL[ch & 0xF]);
}
/**
* Determines the type of quotes (if any) required for the given CharSequence {@code s} based on the YAML format's escaping requirements.
* This method decides between using no quotes, single quotes, or double quotes.
*
* @param s The CharSequence to be analyzed.
* @return The type of quotes required.
*/
@NotNull
protected Quotes needsQuotes(@NotNull CharSequence s) {
@NotNull Quotes quotes = Quotes.NONE;
// Empty strings require double quotes.
if (s.length() == 0)
return Quotes.DOUBLE;
// If string starts with special characters or ends with whitespace, use double quotes.
if (STARTS_QUOTE_CHARS.get(s.charAt(0)) ||
Character.isWhitespace(s.charAt(s.length() - 1)))
return Quotes.DOUBLE;
boolean hasSingleQuote = false;
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
// Characters in QUOTE_CHARS or outside ASCII range need double quotes.
if (QUOTE_CHARS.get(ch) || ch < ' ' || ch > 127)
return Quotes.DOUBLE;
// Track if single quote is present in the string.
if (ch == '\'')
hasSingleQuote = true;
// If a double quote is found followed by a single quote, return double quotes.
if (ch == '"') {
if (i < s.length() - 1 && s.charAt(i + 1) == '\'')
return Quotes.DOUBLE;
quotes = Quotes.SINGLE;
}
}
// If only single quotes are found, no quotes are needed.
if (hasSingleQuote)
return Quotes.NONE;
return quotes;
}
/**
* Appends the given CharSequence {@code cs} to the output bytes using either an 8-bit or UTF-8 encoding, depending on {@code use8bit}.
*
* @param cs CharSequence to be appended.
*/
public void append(@NotNull CharSequence cs) {
if (use8bit)
bytes.append8bit(cs);
else
bytes.appendUtf8(cs);
}
/**
* Appends a subsequence of the given CharSequence {@code cs} to the output bytes using either an 8-bit or UTF-8 encoding.
*
* @param cs CharSequence from which a subsequence will be appended.
* @param offset Starting index of the subsequence.
* @param length Length of the subsequence.
*/
public void append(@NotNull CharSequence cs, int offset, int length) {
if (use8bit)
bytes.append8bit(cs, offset, offset + length);
else
bytes.appendUtf8(cs, offset, length);
}
/**
* Writes the representation of the object {@code o} to the output. Differentiates the serialization logic based on the type of the object.
*
* @param o The object to be serialized.
* @throws InvalidMarshallableException if an error occurs during serialization.
*/
public void writeObject(Object o) throws InvalidMarshallableException {
if (o instanceof Iterable) {
for (Object o2 : (Iterable) o) {
writeObject(o2, 2);
}
} else if (o instanceof Map) {
for (@NotNull Map.Entry