Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
<artifactId>maven-clover2-plugin</artifactId>
<version>4.0.5</version>
<configuration>
<licenseLocation>${user.home}/clover.license</licenseLocation>
<excludes>
<exclude>**/BulletAvro.java</exclude>
</excludes>
Expand Down
65 changes: 65 additions & 0 deletions src/main/java/com/yahoo/bullet/record/BulletRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ public boolean forceReadData() {
}
}

private void forceFailIfCannotRead() {
if (!forceReadData()) {
throw new RuntimeException("Cannot read from record. Unable to proceed.");
}
}

private byte[] serialize(Map<String, Object> data) throws IOException {
data = (data == null) ? Collections.emptyMap() : data;
BulletAvro record = new BulletAvro(data);
Expand Down Expand Up @@ -254,6 +260,65 @@ public Object get(String field, int index) {
return casted.get(index);
}

/**
* Returns true iff the given top-level field exists in the record.
*
* @param field The field to check if it exists.
* @return A boolean denoting whether there was a mapping for the field.
*/
public boolean hasField(String field) {
forceFailIfCannotRead();
return data.containsKey(field);
}

/**
* Returns the number of fields in the record.
*
* @return An int representing the number of fields stored.
*/
public int fieldCount() {
forceFailIfCannotRead();
return data.size();
}

/**
* Removes and returns a top-level field from the record.
*
* @param field The field to remove from the record.
* @return The removed object or null.
*/
public Object getAndRemove(String field) {
// Use hasField to deserialize if necessary
return hasField(field) ? data.remove(field) : null;
}

/**
* Removes a top-level field from the record.
*
* @param field The field to remove from the record.
* @return This object for chaining.
*/
public BulletRecord remove(String field) {
if (hasField(field)) {
data.remove(field);
}
return this;
}

/**
* Renames a top-level field in the record.
*
* @param field The non-null original field name.
* @param newName The non-null new name.
* @return This object for chaining.
*/
public BulletRecord rename(String field, String newName) {
if (hasField(field)) {
set(newName, getAndRemove(field));
}
return this;
}

// ******************************************** SETTERS ********************************************

/**
Expand Down
101 changes: 101 additions & 0 deletions src/test/java/com/yahoo/bullet/record/BulletRecordTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -618,6 +618,22 @@ public void testSerializationWithMapAndRawByteArray() throws IOException {
Assert.assertTrue(record.equals(reified));
}

@Test
public void testBackingByteArrayIfDeserialized() throws IOException {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34);
byte[] rawDataBytes = record.getAsByteArray();

BulletRecord record = new BulletRecord(rawDataBytes);

Assert.assertEquals(record.getAsByteArray(), rawDataBytes);

Assert.assertEquals(record.get("1"), "bar");
Assert.assertEquals(record.get("2"), 42L);
Assert.assertEquals(record.get("3"), false);
Assert.assertEquals(record.get("4"), 0.34);
Assert.assertEquals(record.fieldCount(), 4);
}

@Test
public void testSameByteArrayPostSerialization() throws IOException {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34);
Expand Down Expand Up @@ -655,4 +671,89 @@ public void testCopyRecordWithMap() {

Assert.assertEquals(new BulletRecord(contents), record);
}

@Test
public void testRenaming() {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true))
.setListOfLongMap("9", singletonList(singletonMap("9.1", 3L)));

record.rename("1", "new1").rename("3", "new3").rename("7.4.1", "new2");

BulletRecord expected = new BulletRecord().setString("new1", "bar").setLong("2", 42L).setBoolean("new3", false)
.setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true))
.setListOfLongMap("9", singletonList(singletonMap("9.1", 3L)));

Assert.assertTrue(expected.equals(record));
}

@Test
public void testFieldCount() throws IOException {
Assert.assertEquals(record.fieldCount(), 0);

record.setString("foo", "bar");
Assert.assertEquals(record.fieldCount(), 1);

record.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true));
Assert.assertEquals(record.fieldCount(), 2);

record.remove("2");
record.remove("7");
Assert.assertEquals(record.fieldCount(), 1);

BulletRecord another = new BulletRecord(record.getAsByteArray());
Assert.assertEquals(another.fieldCount(), 1);
}

@Test
public void testRemoving() {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true))
.setListOfLongMap("9", singletonList(singletonMap("9.1", 3L)));

record.remove("1").remove("3").remove("7.4.1").remove("9");

BulletRecord expected = new BulletRecord().setLong("2", 42L).setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true));
Assert.assertTrue(expected.equals(record));
}

@Test
public void testRemovingField() {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true))
.setListOfLongMap("9", singletonList(singletonMap("9.1", 3L)));

Object data;
data = record.getAndRemove("1");
Assert.assertTrue(data instanceof String);
Assert.assertEquals(data, "bar");

data = record.getAndRemove("3");
Assert.assertTrue(data instanceof Boolean);
Assert.assertEquals(data, false);

data = record.getAndRemove("7.7.2");
Assert.assertNull(data);
}

@Test
public void testFieldPresence() {
record.setString("1", "bar").setLong("2", 42L).setBoolean("3", false).setDouble("4", 0.34)
.setMap("7", Pair.of("4.1", false), Pair.of("7.2", true));

Assert.assertTrue(record.hasField("1"));
Assert.assertTrue(record.hasField("7"));
Assert.assertFalse(record.hasField("7.4.1"));
Assert.assertFalse(record.hasField("foo"));
}

@Test(expectedExceptions = RuntimeException.class, expectedExceptionsMessageRegExp = "Cannot read from record.*")
public void testFailingWhenCannotRead() {
record = new BulletRecord();
record.setSerializedData("foo".getBytes());
record.setDeserialized(false);
record.hasField("foo");
}
}