it.auties.protobuf.parser.tree.body.object.ProtobufObjectTree Maven / Gradle / Ivy
The newest version!
package it.auties.protobuf.parser.tree.body.object;
import it.auties.protobuf.parser.ProtobufParserException;
import it.auties.protobuf.parser.tree.ProtobufTree;
import it.auties.protobuf.parser.tree.body.ProtobufBodyTree;
import it.auties.protobuf.parser.tree.nested.option.ProtobufOptionTree;
import java.util.*;
import java.util.stream.Collectors;
public sealed class ProtobufObjectTree, C extends ProtobufTree> extends ProtobufBodyTree
permits ProtobufEnumTree, ProtobufGroupTree, ProtobufMessageTree {
private final LinkedList reserved;
private final LinkedList extensions;
protected ProtobufObjectTree(int line, String name) {
super(line, name);
this.reserved = new LinkedList<>();
this.extensions = new LinkedList<>();
}
public List reserved() {
return Collections.unmodifiableList(reserved);
}
public Optional pollReserved() {
return Optional.ofNullable(reserved.pollLast());
}
public List extensions() {
return Collections.unmodifiableList(extensions);
}
public Optional pollExtensions() {
return Optional.ofNullable(extensions.pollLast());
}
public void addReservedRange(int min) {
var range = new ReservedRange();
range.setMin(min);
this.reserved.add(range);
}
public boolean addReservedIndex(int value) {
if(hasReservedIndex(value)) {
return false;
}
var indexes = new ReservedIndexes();
indexes.addValue(value);
this.reserved.add(indexes);
return true;
}
public boolean addReservedName(String value) {
if(hasReservedName(value)) {
return false;
}
var names = new ReservedNames();
names.addValue(value);
this.reserved.add(names);
return true;
}
public void addExtensionsRange(int min) {
var range = new ExtensionsRange();
range.setMin(min);
this.extensions.add(range);
}
public boolean addExtensionsIndex(int value) {
if(hasExtensionsIndex(value)) {
return false;
}
var indexes = new ExtensionsIndexes();
indexes.addValue(value);
this.extensions.add(indexes);
return true;
}
public sealed interface Reserved {
boolean isAttributed();
void setAttributed(boolean attributed);
}
public boolean hasReservedIndex(int value) {
return reserved.stream().anyMatch(entry -> switch (entry) {
case ProtobufObjectTree.ReservedIndexes reservedIndexes -> reservedIndexes.hasValue(value);
case ProtobufObjectTree.ReservedRange reservedRange -> reservedRange.hasValue(value);
case ProtobufObjectTree.ReservedNames ignored -> false;
});
}
public boolean hasReservedName(String value) {
return value != null && reserved.stream()
.anyMatch(entry -> entry instanceof ProtobufObjectTree, ?>.ReservedNames reservedNames && reservedNames.hasValue(value));
}
public boolean hasExtensionsIndex(int value) {
return extensions.stream().anyMatch(entry -> switch (entry) {
case ProtobufObjectTree.ExtensionsIndexes extensionsIndexes -> extensionsIndexes.hasValue(value);
case ProtobufObjectTree.ExtensionsRange extensionsRange -> extensionsRange.hasValue(value);
});
}
public final class ReservedRange implements Reserved {
private Integer min;
private Integer max;
public OptionalInt min() {
return min == null ? OptionalInt.empty() : OptionalInt.of(min);
}
public boolean setMin(Integer min) {
this.min = min;
return true;
}
public OptionalInt max() {
return max == null ? OptionalInt.empty() : OptionalInt.of(max);
}
public boolean setMax(Integer max) {
if (hasReservedIndex(max)) {
return false;
}
this.max = max;
return true;
}
public boolean hasValue(int entry) {
return (min != null && entry >= min) && (max != null && entry <= max);
}
@Override
public boolean isAttributed() {
return min != null && max != null;
}
@Override
public void setAttributed(boolean attributed) {
if (!attributed) {
return;
}
if(min == null) {
throw new ProtobufParserException("Min");
}
if(max == null) {
throw new ProtobufParserException("Max");
}
}
@Override
public String toString() {
return "%s to %s".formatted(min, max != null && max == Integer.MAX_VALUE ? "max" : max);
}
}
public final class ReservedIndexes implements Reserved {
private final LinkedList values;
private boolean attributed;
private ReservedIndexes() {
this.values = new LinkedList<>();
}
public boolean hasValue(int value) {
return values.contains(value);
}
public boolean addValue(int value) {
if(hasReservedIndex(value)) {
return false;
}
values.add(value);
return true;
}
public boolean isEmpty() {
return values.isEmpty();
}
public Integer pollLastValue() {
return values.pollLast();
}
@Override
public boolean isAttributed() {
return attributed;
}
public void setAttributed(boolean attributed) {
this.attributed = attributed;
}
@Override
public String toString() {
return values.stream().map(String::valueOf).collect(Collectors.joining(", "));
}
public Collection values() {
return Collections.unmodifiableCollection(values);
}
}
public final class ReservedNames implements Reserved {
private final Collection values;
private boolean attributed;
private ReservedNames() {
this.values = new ArrayList<>();
}
public boolean hasValue(String value) {
return values.contains(value);
}
public boolean addValue(String value) {
if(hasReservedName(value)) {
return false;
}
values.add(value);
return true;
}
@Override
public boolean isAttributed() {
return attributed;
}
public void setAttributed(boolean attributed) {
this.attributed = attributed;
}
@Override
public String toString() {
return values.stream()
.map("\"%s\""::formatted)
.collect(Collectors.joining(", "));
}
public Collection values() {
return Collections.unmodifiableCollection(values);
}
}
public sealed interface Extensions {
boolean isAttributed();
void setAttributed(boolean attributed);
}
public final class ExtensionsRange implements Extensions {
private Integer min;
private Integer max;
public OptionalInt min() {
return min == null ? OptionalInt.empty() : OptionalInt.of(min);
}
public boolean setMin(Integer min) {
this.min = min;
return true;
}
public OptionalInt max() {
return max == null ? OptionalInt.empty() : OptionalInt.of(max);
}
public boolean setMax(Integer max) {
if(hasExtensionsIndex(max)) {
return false;
}
this.max = max;
return true;
}
private boolean hasValue(Integer entry) {
return (min != null && entry >= min) && (max != null && entry <= max);
}
@Override
public boolean isAttributed() {
return min != null && max != null;
}
@Override
public void setAttributed(boolean attributed) {
if (!attributed) {
return;
}
if(min == null) {
throw new ProtobufParserException("Min");
}
if(max == null) {
throw new ProtobufParserException("Max");
}
}
@Override
public String toString() {
return "%s to %s".formatted(min, max == Integer.MAX_VALUE ? "max" : max);
}
}
public final class ExtensionsIndexes implements Extensions {
private final LinkedList values;
private boolean attributed;
private ExtensionsIndexes() {
this.values = new LinkedList<>();
}
public boolean hasValue(int value) {
return values.contains(value);
}
public boolean addValue(int value) {
if(hasExtensionsIndex(value)) {
return false;
}
values.add(value);
return true;
}
public boolean isEmpty() {
return values.isEmpty();
}
public Integer pollLastValue() {
return values.pollLast();
}
@Override
public boolean isAttributed() {
return attributed;
}
public void setAttributed(boolean attributed) {
this.attributed = attributed;
}
@Override
public String toString() {
return values.stream().map(String::valueOf).collect(Collectors.joining(", "));
}
public Collection values() {
return Collections.unmodifiableCollection(values);
}
}
@Override
public String toString() {
var builder = new StringBuilder();
builder.append(header());
options.values()
.stream()
.map(ProtobufOptionTree::toString)
.forEach(option -> {
builder.append(option);
builder.append("\n");
});
reserved.stream()
.map(entry -> toLeveledString("reserved " + entry + ";\n", this instanceof ProtobufGroupTree ? 0 : 1))
.forEach(builder::append);
extensions.stream()
.map(entry -> toLeveledString("extensions " + entry + ";\n", this instanceof ProtobufGroupTree ? 0 : 1))
.forEach(builder::append);
statements().forEach(statement -> {
builder.append(statement.toString());
builder.append("\n");
});
builder.append(toLeveledString("}", this instanceof ProtobufGroupTree ? -1 : 0));
return builder.toString();
}
private String header() {
if(this instanceof ProtobufGroupTree) {
return "{\n";
}
var instructionName = switch (this) {
case ProtobufMessageTree ignored -> "message";
case ProtobufEnumTree ignored -> "enum";
default -> throw new IllegalStateException("Unexpected value: " + this);
};
var name = Objects.requireNonNullElse(this.name, "[missing]");
return toLeveledString("%s %s {\n".formatted(instructionName, name));
}
}