Skip to content

Commit 9ee9930

Browse files
committed
sort stringified set elements with one field
Stringified set elements are now outputted in sorted order when each element has exactly one field. Previously the output order was determined by the element ordinals.
1 parent a64f19e commit 9ee9930

File tree

6 files changed

+184
-10
lines changed

6 files changed

+184
-10
lines changed

hollow/src/main/java/com/netflix/hollow/core/read/HollowReadFieldUtils.java

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@
1919
import com.netflix.hollow.core.memory.encoding.HashCodes;
2020
import com.netflix.hollow.core.read.dataaccess.HollowDataAccess;
2121
import com.netflix.hollow.core.read.dataaccess.HollowObjectTypeDataAccess;
22+
import com.netflix.hollow.core.read.dataaccess.HollowTypeDataAccess;
2223
import com.netflix.hollow.core.schema.HollowObjectSchema;
2324
import java.util.Arrays;
2425

2526
/**
26-
* Useful utility methods for interacting with a {@link HollowDataAccess}.
27+
* Useful utility methods for interacting with a {@link HollowDataAccess}.
2728
*/
2829
public class HollowReadFieldUtils {
2930

@@ -142,7 +143,7 @@ public static boolean fieldsAreEqual(HollowObjectTypeDataAccess typeAccess1, int
142143
* @param typeAccess the type access
143144
* @param ordinal the ordinal
144145
* @param fieldPosition the field position
145-
* @return a displayable String for a field from an OBJECT record.
146+
* @return a displayable String for a field from an OBJECT record.
146147
*/
147148
public static String displayString(HollowObjectTypeDataAccess typeAccess, int ordinal, int fieldPosition) {
148149
HollowObjectSchema schema = typeAccess.getSchema();
@@ -263,6 +264,76 @@ public static boolean fieldValueEquals(HollowObjectTypeDataAccess typeAccess, in
263264
throw new IllegalStateException("I don't know how to test equality for a " + schema.getFieldType(fieldPosition));
264265
}
265266

267+
/**
268+
*
269+
* @param typeAccess the type access
270+
* @param fieldPosition the field position
271+
* @param ordinal1 the first object ordinal
272+
* @param ordinal2 the second object ordinal
273+
* @return compareTo result for the two field values at the specified field position
274+
*/
275+
public static int compareFieldValues(HollowObjectTypeDataAccess typeAccess, int fieldPosition, int ordinal1, int ordinal2) {
276+
HollowObjectSchema schema = typeAccess.getSchema();
277+
278+
switch(schema.getFieldType(fieldPosition)) {
279+
case BOOLEAN:
280+
Boolean bool1 = typeAccess.readBoolean(ordinal1, fieldPosition);
281+
Boolean bool2 = typeAccess.readBoolean(ordinal2, fieldPosition);
282+
return Boolean.compare(bool1, bool2);
283+
case BYTES:
284+
// TODO: When upgrading > Java 8, consider using Arrays.compare() instead.
285+
byte[] data1 = typeAccess.readBytes(ordinal1, fieldPosition);
286+
byte[] data2 = typeAccess.readBytes(ordinal2, fieldPosition);
287+
for(int i=0;i<data1.length;i++) {
288+
if(i >= data2.length)
289+
return 1;
290+
int cmp = Byte.compare(data1[i], data2[i]);
291+
if(cmp != 0)
292+
return cmp;
293+
}
294+
if(data1.length < data2.length)
295+
return -1;
296+
return 0;
297+
case DOUBLE:
298+
double d1 = typeAccess.readDouble(ordinal1, fieldPosition);
299+
double d2 = typeAccess.readDouble(ordinal2, fieldPosition);
300+
return Double.compare(d1, d2);
301+
case FLOAT:
302+
float f1 = typeAccess.readFloat(ordinal1, fieldPosition);
303+
float f2 = typeAccess.readFloat(ordinal2, fieldPosition);
304+
return Float.compare(f1, f2);
305+
case INT:
306+
int i1 = typeAccess.readInt(ordinal1, fieldPosition);
307+
int i2 = typeAccess.readInt(ordinal2, fieldPosition);
308+
return Integer.compare(i1, i2);
309+
case LONG:
310+
long l1 = typeAccess.readLong(ordinal1, fieldPosition);
311+
long l2 = typeAccess.readLong(ordinal2, fieldPosition);
312+
return Long.compare(l1, l2);
313+
case STRING:
314+
String s1 = typeAccess.readString(ordinal1, fieldPosition);
315+
String s2 = typeAccess.readString(ordinal2, fieldPosition);
316+
return s1.compareTo(s2);
317+
case REFERENCE:
318+
// For reference types, if the referenced type is an object,
319+
// then compare the first field of the object, otherwise assume
320+
// equality.
321+
HollowTypeDataAccess typeDataAccess = typeAccess.getDataAccess().getTypeDataAccess(schema.getReferencedType(fieldPosition));
322+
if (typeDataAccess instanceof HollowObjectTypeDataAccess) {
323+
HollowObjectTypeDataAccess refTypeAccess = (HollowObjectTypeDataAccess) typeDataAccess;
324+
HollowObjectSchema refSchema = refTypeAccess.getSchema();
325+
if (refSchema.numFields() == 1) {
326+
return compareFieldValues(refTypeAccess, 0,
327+
typeAccess.readOrdinal(ordinal1, fieldPosition),
328+
typeAccess.readOrdinal(ordinal2, fieldPosition));
329+
}
330+
}
331+
return 0;
332+
}
333+
334+
throw new IllegalStateException("I don't know how to compare a " + schema.getFieldType(fieldPosition));
335+
}
336+
266337
/**
267338
* @param data the byte array
268339
* @return The hash code for a byte array which would be returned from {@link #fieldHashCode(HollowObjectTypeDataAccess, int, int)}

hollow/src/main/java/com/netflix/hollow/tools/stringifier/HollowRecordJsonStringifier.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.netflix.hollow.tools.stringifier;
1818

1919
import static com.netflix.hollow.core.HollowConstants.ORDINAL_NONE;
20+
import static com.netflix.hollow.core.read.HollowReadFieldUtils.compareFieldValues;
2021

2122
import com.netflix.hollow.api.objects.HollowRecord;
2223
import com.netflix.hollow.core.read.dataaccess.HollowDataAccess;
@@ -40,6 +41,7 @@
4041
import java.util.Collections;
4142
import java.util.HashSet;
4243
import java.util.Iterator;
44+
import java.util.List;
4345
import java.util.Set;
4446

4547
/**
@@ -284,15 +286,31 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess, Holl
284286

285287
int elementOrdinal = iter.next();
286288

287-
if (elementOrdinal == HollowOrdinalIterator.NO_MORE_ORDINALS) {
289+
290+
List<Integer> elementOrdinals = new java.util.ArrayList<>();
291+
while (elementOrdinal != HollowOrdinalIterator.NO_MORE_ORDINALS) {
292+
elementOrdinals.add(elementOrdinal);
293+
elementOrdinal = iter.next();
294+
}
295+
296+
HollowTypeDataAccess elementTypeDataAccess = dataAccess.getTypeDataAccess(elementType);
297+
if (elementTypeDataAccess instanceof HollowObjectTypeDataAccess) {
298+
HollowObjectSchema elementSchema = ((HollowObjectTypeDataAccess) elementTypeDataAccess).getSchema();
299+
if (elementSchema.numFields() == 1) {
300+
elementOrdinals.sort((o1, o2) ->
301+
compareFieldValues((HollowObjectTypeDataAccess) elementTypeDataAccess, 0, o1, o2));
302+
}
303+
}
304+
305+
if (elementOrdinals.isEmpty()) {
288306
writer.append("[]");
289307
} else {
290308
boolean firstElement = true;
291309
writer.append("[");
292310
if (prettyPrint)
293311
writer.append(NEWLINE);
294312

295-
while(elementOrdinal != HollowOrdinalIterator.NO_MORE_ORDINALS) {
313+
for (int ord : elementOrdinals) {
296314
if (firstElement)
297315
firstElement = false;
298316
else
@@ -301,9 +319,7 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess, Holl
301319
if (prettyPrint)
302320
appendIndentation(writer, indentation);
303321

304-
appendStringify(writer, dataAccess, elementType, elementOrdinal, indentation);
305-
306-
elementOrdinal = iter.next();
322+
appendStringify(writer, dataAccess, elementType, ord, indentation);
307323
}
308324

309325
if (prettyPrint) {

hollow/src/main/java/com/netflix/hollow/tools/stringifier/HollowRecordStringifier.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
package com.netflix.hollow.tools.stringifier;
1818

19+
import static com.netflix.hollow.core.read.HollowReadFieldUtils.compareFieldValues;
1920
import static com.netflix.hollow.core.read.iterator.HollowOrdinalIterator.NO_MORE_ORDINALS;
2021

2122
import com.netflix.hollow.api.objects.HollowRecord;
@@ -38,6 +39,7 @@
3839
import java.util.Arrays;
3940
import java.util.HashSet;
4041
import java.util.Iterator;
42+
import java.util.List;
4143
import java.util.Set;
4244

4345
/**
@@ -184,15 +186,28 @@ private void appendSetStringify(Writer writer, HollowDataAccess dataAccess,
184186

185187
int elementOrdinal = iter.next();
186188

189+
List<Integer> elementOrdinals = new java.util.ArrayList<>();
187190
while(elementOrdinal != NO_MORE_ORDINALS) {
191+
elementOrdinals.add(elementOrdinal);
192+
elementOrdinal = iter.next();
193+
}
194+
195+
HollowTypeDataAccess elementTypeDataAccess = dataAccess.getTypeDataAccess(elementType);
196+
if (elementTypeDataAccess instanceof HollowObjectTypeDataAccess) {
197+
HollowObjectSchema elementSchema = ((HollowObjectTypeDataAccess) elementTypeDataAccess).getSchema();
198+
if (elementSchema.numFields() == 1) {
199+
elementOrdinals.sort((o1, o2) ->
200+
compareFieldValues((HollowObjectTypeDataAccess) elementTypeDataAccess, 0, o1, o2));
201+
}
202+
}
203+
204+
for (Integer sortedElementOrd : elementOrdinals) {
188205
writer.append(NEWLINE);
189206

190207
appendIndentation(writer, indentation);
191208
writer.append("e: ");
192209

193-
appendStringify(writer, dataAccess, elementType, elementOrdinal, indentation);
194-
195-
elementOrdinal = iter.next();
210+
appendStringify(writer, dataAccess, elementType, sortedElementOrd, indentation);
196211
}
197212
}
198213

hollow/src/test/java/com/netflix/hollow/tools/stringifier/AbstractHollowRecordStringifierTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.netflix.hollow.core.write.objectmapper.HollowObjectMapper;
2525
import java.io.IOException;
2626
import java.io.StringWriter;
27+
import java.util.Set;
2728

2829
/**
2930
* Code shared between HollowRecordStringifierTest and HollowRecordJsonStringifierTest.
@@ -73,6 +74,21 @@ public TypeWithNestedNonPrimitive(Double value, TypeWithNonPrimitive nestedType)
7374
}
7475
}
7576

77+
static class TypeWithSetOfPrimitives {
78+
private final Set<TypeWithPrimitive> values;
79+
80+
public TypeWithSetOfPrimitives(Set<TypeWithPrimitive> values) {
81+
this.values = values;
82+
}
83+
}
84+
85+
static class TypeWithSetOfStrings {
86+
private final Set<TypeWithString> values;
87+
88+
public TypeWithSetOfStrings(Set<TypeWithString> values) {
89+
this.values = values;
90+
}
91+
}
7692

7793
/**
7894
* Sends instances of a type through the HollowRecordStringifier. This concatenates records

hollow/src/test/java/com/netflix/hollow/tools/stringifier/HollowRecordJsonStringifierTest.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,32 @@ public void testStringifyStringWithEscapeChars() throws IOException {
206206
stringifyType(TypeWithString.class, true, false, new TypeWithString(testString)));
207207
}
208208

209+
@Test
210+
public void testStringifySetOfPrimitives() throws IOException {
211+
Assert.assertEquals("Set of primitives should be printed correctly",
212+
"[3,200,1000]",
213+
stringifyType(TypeWithSetOfPrimitives.class, false, false,
214+
new TypeWithSetOfPrimitives(
215+
new java.util.HashSet<>(Arrays.asList(
216+
new TypeWithPrimitive(1000),
217+
new TypeWithPrimitive(3),
218+
new TypeWithPrimitive(200)
219+
)))));
220+
}
221+
222+
@Test
223+
public void testStringifySetOfStrings() throws IOException {
224+
Assert.assertEquals("Set of strings should be printed correctly",
225+
"[\"a\",\"b\",\"c\"]",
226+
stringifyType(TypeWithSetOfStrings.class, false, false,
227+
new TypeWithSetOfStrings(
228+
new java.util.HashSet<>(Arrays.asList(
229+
new TypeWithString("c"),
230+
new TypeWithString("a"),
231+
new TypeWithString("b")
232+
)))));
233+
}
234+
209235
private static <T> String stringifyType(Class<T> clazz, boolean prettyPrint, boolean expanded, T... instances) throws IOException {
210236
HollowRecordJsonStringifier stringifier = new HollowRecordJsonStringifier(prettyPrint, !expanded);
211237
// HollowRecordJsonStringifier stringifier = expanded

hollow/src/test/java/com/netflix/hollow/tools/stringifier/HollowRecordStringifierTest.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,36 @@ public void testStringifyIterator() throws IOException {
135135
"\n]", writer.toString());
136136
}
137137

138+
@Test
139+
public void testStringifySetOfPrimitives() throws IOException {
140+
Assert.assertEquals("Set of primitives should be printed correctly",
141+
"\n e: 3\n" +
142+
" e: 200\n" +
143+
" e: 1000",
144+
stringifyType(TypeWithSetOfPrimitives.class, false,
145+
new TypeWithSetOfPrimitives(
146+
new java.util.HashSet<>(Arrays.asList(
147+
new TypeWithPrimitive(1000),
148+
new TypeWithPrimitive(3),
149+
new TypeWithPrimitive(200)
150+
)))));
151+
}
152+
153+
@Test
154+
public void testStringifySetOfStrings() throws IOException {
155+
Assert.assertEquals("Set of strings should be printed correctly",
156+
"\n e: a\n" +
157+
" e: b\n" +
158+
" e: c",
159+
stringifyType(TypeWithSetOfStrings.class, false,
160+
new TypeWithSetOfStrings(
161+
new java.util.HashSet<>(Arrays.asList(
162+
new TypeWithString("c"),
163+
new TypeWithString("a"),
164+
new TypeWithString("b")
165+
)))));
166+
}
167+
138168
private static <T> String stringifyType(Class<T> clazz, boolean expanded, T... instances) throws IOException {
139169
HollowRecordStringifier stringifier = expanded
140170
? new HollowRecordStringifier(true, true, false) : new HollowRecordStringifier();

0 commit comments

Comments
 (0)