Added working serialization and starter javafx framework.
This commit is contained in:
parent
00c343e673
commit
7bd89c0e50
|
@ -1,7 +1,60 @@
|
|||
package nl.andrewlalis.crystalkeep;
|
||||
|
||||
public class CrystalKeep {
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Hello world!");
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.serialization.ClusterSerializer;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class CrystalKeep extends Application {
|
||||
public static void main(String[] args) throws IOException {
|
||||
launch(args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Stage stage) throws Exception {
|
||||
URL url = CrystalKeep.class.getClassLoader().getResource("ui/crystalkeep.fxml");
|
||||
FXMLLoader loader = new FXMLLoader(url);
|
||||
var scene = new Scene(loader.load());
|
||||
stage.setScene(scene);
|
||||
stage.setTitle("CrystalKeep");
|
||||
stage.sizeToScene();
|
||||
stage.show();
|
||||
}
|
||||
|
||||
public static void test() throws IOException {
|
||||
Cluster c = new Cluster("Test");
|
||||
Shard s = new TextShard(c, "sample", LocalDateTime.now(), "Hello world!");
|
||||
Shard s2 = new LoginCredentialsShard(c, "logs", LocalDateTime.now().plusHours(3), "andrew", "testing");
|
||||
c.addShard(s);
|
||||
c.addShard(s2);
|
||||
Cluster c2 = new Cluster("Test2");
|
||||
Shard s3 = new TextShard(c2, "another sample", LocalDateTime.now().plusMinutes(3), "Testing this stuff....");
|
||||
c2.addShard(s3);
|
||||
Cluster parent = new Cluster("Parent");
|
||||
parent.addCluster(c);
|
||||
parent.addCluster(c2);
|
||||
|
||||
System.out.println(parent);
|
||||
long start = System.currentTimeMillis();
|
||||
byte[] cBytes = ClusterSerializer.toBytes(parent);
|
||||
long dur = System.currentTimeMillis() - start;
|
||||
System.out.println("Duration: " + dur + " milliseconds");
|
||||
System.out.println(Arrays.toString(cBytes));
|
||||
|
||||
Cluster cLoaded = ClusterSerializer.clusterFromBytes(new ByteArrayInputStream(cBytes), null);
|
||||
System.out.println(cLoaded);
|
||||
System.out.println(Arrays.toString(ClusterSerializer.toBytes(cLoaded)));
|
||||
System.out.println(Arrays.equals(cBytes, ClusterSerializer.toBytes(cLoaded)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,41 +1,71 @@
|
|||
package nl.andrewlalis.crystalkeep.model;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
public class Cluster {
|
||||
public class Cluster implements Comparable<Cluster> {
|
||||
@Setter
|
||||
private String name;
|
||||
private final Set<Shard> shards;
|
||||
private final Set<Cluster> clusters;
|
||||
private final Cluster parent;
|
||||
@Setter
|
||||
private Cluster parent;
|
||||
|
||||
public Cluster(Set<Shard> shards, Set<Cluster> clusters, Cluster parent) {
|
||||
public Cluster(String name, Set<Shard> shards, Set<Cluster> clusters, Cluster parent) {
|
||||
this.name = name;
|
||||
this.shards = shards;
|
||||
this.clusters = clusters;
|
||||
this.parent = parent;
|
||||
if (this.parent != null) {
|
||||
this.parent.addCluster(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Cluster() {
|
||||
this(new HashSet<>(), new HashSet<>(), null);
|
||||
public Cluster(String name) {
|
||||
this(name, new HashSet<>(), new HashSet<>(), null);
|
||||
}
|
||||
|
||||
public void addShard(Shard shard) {
|
||||
if (!this.equals(shard.getCluster())) {
|
||||
throw new IllegalArgumentException("Shard must have correct cluster set before adding it to shard.");
|
||||
}
|
||||
this.shards.add(shard);
|
||||
shard.setCluster(this);
|
||||
}
|
||||
|
||||
public void addCluster(Cluster cluster) {
|
||||
if (!this.equals(cluster.getParent())) {
|
||||
throw new IllegalArgumentException("Cluster must have correct parent set before adding it to cluster.");
|
||||
}
|
||||
this.clusters.add(cluster);
|
||||
cluster.setParent(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Cluster o) {
|
||||
return this.getName().compareTo(o.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Cluster cluster = (Cluster) o;
|
||||
return getName().equals(cluster.getName()) && Objects.equals(getParent(), cluster.getParent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getName(), getParent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("Cluster: name=\"").append(this.getName()).append("\"\nShards:\n");
|
||||
for (Shard s : this.getShards()) {
|
||||
sb.append('\t').append(s.toString()).append('\n');
|
||||
}
|
||||
sb.append("Nested clusters:\n");
|
||||
for (Cluster c : this.getClusters()) {
|
||||
sb.append('\n').append(c.toString()).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
package nl.andrewlalis.crystalkeep.model;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class ClusterSerializer {
|
||||
public byte[] toBytes(Cluster cluster) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
bos.write(this.toBytes(cluster.getClusters().size()));
|
||||
for (Cluster child : cluster.getClusters()) {
|
||||
bos.write(this.toBytes(child));
|
||||
}
|
||||
bos.write(this.toBytes(cluster.getShards().size()));
|
||||
for (Shard shard : cluster.getShards()) {
|
||||
bos.write(shard.toBytes());
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public Cluster fromBytes(ByteArrayInputStream bis, Cluster parent) throws IOException {
|
||||
Cluster cluster = new Cluster(new HashSet<>(), new HashSet<>(), parent);
|
||||
int childCount = this.toInt(bis.readNBytes(4));
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
cluster.addCluster(this.fromBytes(bis, cluster));
|
||||
}
|
||||
int shardCount = this.toInt(bis.readNBytes(4));
|
||||
for (int i = 0; i < shardCount; i++) {
|
||||
cluster.addShard(Shard.fromBytes(bis));
|
||||
}
|
||||
return cluster;
|
||||
}
|
||||
|
||||
public byte[] toBytes(int x) {
|
||||
return ByteBuffer.allocate(4).putInt(x).array();
|
||||
}
|
||||
|
||||
public int toInt(byte[] bytes) {
|
||||
assert(bytes.length == 4);
|
||||
return ByteBuffer.wrap(bytes).getInt();
|
||||
}
|
||||
}
|
|
@ -2,22 +2,35 @@ package nl.andrewlalis.crystalkeep.model;
|
|||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.ShardType;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A shard is a single "piece" of information, such as a snippet of text, login
|
||||
* credentials, an image, or a private key. All shards within a cluster should
|
||||
* have unique names.
|
||||
* <p>
|
||||
* Due to the need to deserialize shards from byte arrays, it is required
|
||||
* that this parent class holds a type discriminator value, which is used to
|
||||
* decide which shard type to deserialize.
|
||||
* </p>
|
||||
*/
|
||||
@Getter
|
||||
public abstract class Shard implements Comparable<Shard> {
|
||||
private final Cluster cluster;
|
||||
@Setter
|
||||
private Cluster cluster;
|
||||
@Setter
|
||||
private String name;
|
||||
private final LocalDateTime createdAt;
|
||||
private final ShardType type;
|
||||
|
||||
public Shard(Cluster cluster, String name, LocalDateTime createdAt) {
|
||||
public Shard(Cluster cluster, String name, LocalDateTime createdAt, ShardType type) {
|
||||
this.cluster = cluster;
|
||||
this.cluster.addShard(this);
|
||||
this.name = name;
|
||||
this.createdAt = createdAt;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,11 +40,21 @@ public abstract class Shard implements Comparable<Shard> {
|
|||
return this.getCreatedAt().compareTo(o.getCreatedAt());
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return new byte[0];
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Shard shard = (Shard) o;
|
||||
return getCluster().equals(shard.getCluster()) && getName().equals(shard.getName()) && getType() == shard.getType();
|
||||
}
|
||||
|
||||
public static Shard fromBytes(ByteArrayInputStream bis) {
|
||||
return null;
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(getCluster(), getName(), getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Shard: name=\"" + this.name + "\", type=" + this.type + ", createdAt=" + this.createdAt;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package nl.andrewlalis.crystalkeep.model.serialization;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class ByteUtils {
|
||||
public static byte[] toBytes(int x) {
|
||||
return ByteBuffer.allocate(4).putInt(x).array();
|
||||
}
|
||||
|
||||
public static int toInt(byte[] bytes) {
|
||||
assert(bytes.length == 4);
|
||||
return ByteBuffer.wrap(bytes).getInt();
|
||||
}
|
||||
|
||||
public static void writeLengthPrefixed(byte[] bytes, OutputStream os) throws IOException {
|
||||
os.write(toBytes(bytes.length));
|
||||
os.write(bytes);
|
||||
}
|
||||
|
||||
public static void writeLengthPrefixed(String s, OutputStream os) throws IOException {
|
||||
writeLengthPrefixed(s.getBytes(StandardCharsets.UTF_8), os);
|
||||
}
|
||||
|
||||
public static byte[] writeLengthPrefixedStrings(String[] strings) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
for (String s : strings) {
|
||||
writeLengthPrefixed(s, bos);
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
public static byte[] readLengthPrefixed(InputStream is) throws IOException {
|
||||
int count = toInt(is.readNBytes(4));
|
||||
return is.readNBytes(count);
|
||||
}
|
||||
|
||||
public static String readLengthPrefixedString(InputStream is) throws IOException {
|
||||
return new String(readLengthPrefixed(is), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package nl.andrewlalis.crystalkeep.model.serialization;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.LoginCredentialsShard;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.ShardType;
|
||||
import nl.andrewlalis.crystalkeep.model.shards.TextShard;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
|
||||
import static nl.andrewlalis.crystalkeep.model.serialization.ByteUtils.toInt;
|
||||
|
||||
public class ClusterSerializer {
|
||||
private static final Map<ShardType, ShardSerializer<?>> serializers = new HashMap<>();
|
||||
static {
|
||||
serializers.put(ShardType.TEXT, new TextShard.Serializer());
|
||||
serializers.put(ShardType.LOGIN_CREDENTIALS, new LoginCredentialsShard.Serializer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a cluster to a byte array, including all shards and nested
|
||||
* clusters.
|
||||
* @param cluster The cluster to serialize.
|
||||
* @return The byte array representing the cluster.
|
||||
* @throws IOException If an error occurs while writing the cluster.
|
||||
*/
|
||||
public static byte[] toBytes(Cluster cluster) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ByteUtils.writeLengthPrefixed(cluster.getName(), bos);
|
||||
|
||||
bos.write(ByteUtils.toBytes(cluster.getClusters().size()));
|
||||
Cluster[] children = new Cluster[cluster.getClusters().size()];
|
||||
cluster.getClusters().toArray(children);
|
||||
Arrays.sort(children);
|
||||
for (Cluster child : children) {
|
||||
bos.write(toBytes(child));
|
||||
}
|
||||
|
||||
bos.write(ByteUtils.toBytes(cluster.getShards().size()));
|
||||
Shard[] shards = new Shard[cluster.getShards().size()];
|
||||
cluster.getShards().toArray(shards);
|
||||
Arrays.sort(shards);
|
||||
for (Shard shard : shards) {
|
||||
bos.write(toBytes(shard));
|
||||
}
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a cluster from an input stream.
|
||||
* @param is The input stream to read from.
|
||||
* @param parent The parent cluster of this cluster. This may be null.
|
||||
* @return The cluster that was read.
|
||||
* @throws IOException If data could not be read from the stream.
|
||||
*/
|
||||
public static Cluster clusterFromBytes(InputStream is, Cluster parent) throws IOException {
|
||||
String name = ByteUtils.readLengthPrefixedString(is);
|
||||
Cluster cluster = new Cluster(name, new HashSet<>(), new HashSet<>(), parent);
|
||||
int childCount = toInt(is.readNBytes(4));
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
cluster.addCluster(clusterFromBytes(is, cluster));
|
||||
}
|
||||
int shardCount = toInt(is.readNBytes(4));
|
||||
for (int i = 0; i < shardCount; i++) {
|
||||
cluster.addShard(shardFromBytes(is, cluster));
|
||||
}
|
||||
return cluster;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a shard to a byte array. It does this by first writing the
|
||||
* standard shard information, then using a specific {@link ShardSerializer}
|
||||
* to get the bytes that represent the body of the shard.
|
||||
* @param shard The shard to serialize.
|
||||
* @return A byte array representing the shard.
|
||||
* @throws IOException If byte array stream could not be written to.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public static byte[] toBytes(Shard shard) throws IOException {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ByteUtils.writeLengthPrefixed(shard.getName().getBytes(StandardCharsets.UTF_8), bos);
|
||||
ByteUtils.writeLengthPrefixed(shard.getCreatedAt().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME).getBytes(StandardCharsets.UTF_8), bos);
|
||||
bos.write(ByteUtils.toBytes(shard.getType().getValue()));
|
||||
ShardSerializer serializer = serializers.get(shard.getType());
|
||||
bos.write(serializer.serialize(shard));
|
||||
return bos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a shard from a byte array that's being read by the given
|
||||
* input stream.
|
||||
* @param is The input stream to read from.
|
||||
* @param cluster The cluster that the shard should belong to.
|
||||
* @return The shard that was deserialized.
|
||||
* @throws IOException If an error occurs while reading bytes.
|
||||
*/
|
||||
public static Shard shardFromBytes(InputStream is, Cluster cluster) throws IOException {
|
||||
String name = ByteUtils.readLengthPrefixedString(is);
|
||||
LocalDateTime createdAt = LocalDateTime.parse(ByteUtils.readLengthPrefixedString(is), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
|
||||
ShardType type = ShardType.valueOf(ByteUtils.toInt(is.readNBytes(4)));
|
||||
ShardSerializer<?> serializer = serializers.get(type);
|
||||
if (serializer == null) {
|
||||
throw new IOException("Unsupported shard type.");
|
||||
}
|
||||
return serializer.deserialize(is, cluster, name, createdAt);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package nl.andrewlalis.crystalkeep.model.serialization;
|
||||
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public interface ShardSerializer<T extends Shard> {
|
||||
byte[] serialize(T shard) throws IOException;
|
||||
|
||||
T deserialize(InputStream bis, Cluster cluster, String name, LocalDateTime createdAt) throws IOException;
|
||||
}
|
|
@ -4,7 +4,11 @@ import lombok.Getter;
|
|||
import lombok.Setter;
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils;
|
||||
import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
|
@ -14,8 +18,28 @@ public class LoginCredentialsShard extends Shard {
|
|||
private String password;
|
||||
|
||||
public LoginCredentialsShard(Cluster cluster, String name, LocalDateTime createdAt, String username, String password) {
|
||||
super(cluster, name, createdAt);
|
||||
super(cluster, name, createdAt, ShardType.LOGIN_CREDENTIALS);
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ", username=\"" + this.username + "\", password=\"" + this.password + "\"";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<LoginCredentialsShard> {
|
||||
|
||||
@Override
|
||||
public byte[] serialize(LoginCredentialsShard shard) throws IOException {
|
||||
return ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getUsername(), shard.getPassword()});
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginCredentialsShard deserialize(InputStream is, Cluster cluster, String name, LocalDateTime createdAt) throws IOException {
|
||||
String username = ByteUtils.readLengthPrefixedString(is);
|
||||
String password = ByteUtils.readLengthPrefixedString(is);
|
||||
return new LoginCredentialsShard(cluster, name, createdAt, username, password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package nl.andrewlalis.crystalkeep.model.shards;
|
||||
|
||||
import lombok.Getter;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
|
||||
/**
|
||||
* Represents a distinct type of shard, and should correspond to exactly one
|
||||
* subtype of {@link Shard}.
|
||||
* <p>
|
||||
* All types defined here should have a unique integer value, which is used
|
||||
* for serialization and deserialization of shards.
|
||||
* </p>
|
||||
*/
|
||||
public enum ShardType {
|
||||
/**
|
||||
* Represents a {@link TextShard}
|
||||
*/
|
||||
TEXT(1),
|
||||
|
||||
/**
|
||||
* Represents a {@link LoginCredentialsShard}
|
||||
*/
|
||||
LOGIN_CREDENTIALS(2);
|
||||
|
||||
@Getter
|
||||
private final int value;
|
||||
|
||||
ShardType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public static ShardType valueOf(int value) {
|
||||
for (var type : values()) {
|
||||
if (type.getValue() == value) return type;
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid value.");
|
||||
}
|
||||
}
|
|
@ -1,15 +1,39 @@
|
|||
package nl.andrewlalis.crystalkeep.model.shards;
|
||||
|
||||
import lombok.Getter;
|
||||
import nl.andrewlalis.crystalkeep.model.Cluster;
|
||||
import nl.andrewlalis.crystalkeep.model.Shard;
|
||||
import nl.andrewlalis.crystalkeep.model.serialization.ByteUtils;
|
||||
import nl.andrewlalis.crystalkeep.model.serialization.ShardSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Getter
|
||||
public class TextShard extends Shard {
|
||||
private String text;
|
||||
|
||||
public TextShard(Cluster cluster, String name, LocalDateTime createdAt, String text) {
|
||||
super(cluster, name, createdAt);
|
||||
super(cluster, name, createdAt, ShardType.TEXT);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + ", text=\"" + this.text + "\"";
|
||||
}
|
||||
|
||||
public static class Serializer implements ShardSerializer<TextShard> {
|
||||
@Override
|
||||
public byte[] serialize(TextShard shard) throws IOException {
|
||||
return ByteUtils.writeLengthPrefixedStrings(new String[]{shard.getText()});
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextShard deserialize(InputStream is, Cluster cluster, String name, LocalDateTime createdAt) throws IOException {
|
||||
String text = ByteUtils.readLengthPrefixedString(is);
|
||||
return new TextShard(cluster, name, createdAt, text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,14 +3,8 @@
|
|||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
|
||||
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<center>
|
||||
<TabPane prefHeight="200.0" prefWidth="200.0" tabClosingPolicy="UNAVAILABLE" BorderPane.alignment="CENTER">
|
||||
<tabs>
|
||||
<Tab text="Shards" />
|
||||
<Tab text="Untitled Tab 2" />
|
||||
</tabs>
|
||||
</TabPane>
|
||||
</center>
|
||||
<left>
|
||||
<TreeView prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
|
||||
</left>
|
||||
</BorderPane>
|
Loading…
Reference in New Issue