Skip to content

Commit fc769e9

Browse files
authored
Merge pull request #15 from gharris1727/iterator-initializer
CC-7198: Add iterator initializer schema field
2 parents be1ef14 + 8346eda commit fc769e9

File tree

4 files changed

+123
-12
lines changed

4 files changed

+123
-12
lines changed

README.md

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,19 @@ read from the file after decoding with the specified format (currently
7676
"json" and "binary" are the only supported values, and "binary" may be
7777
somewhat buggy).
7878
+ __iteration:__ A JSON object that conforms to the following format:
79-
`{"start": <start>, "restart": <restart>, "step": <step>}` ("start" has
80-
to be specified, but "restart" and "step" do not). If provided with a
79+
`{"start": <start>, "restart": <restart>, "step": <step>, "initial":
80+
<initial> }` ("start" has to be specified, but "restart", "step",
81+
and "initial" do not). If provided with a
8182
numeric schema, ensures that the first generated value will be equal to
82-
&lt;start&gt;, and successive values will increase by &lt;step&gt;,
83-
wrapping around at &lt;restart&gt;; &lt;step&gt; will default to 1 if
84-
&lt;restart&gt; is greater than &lt;step&gt;, will default to -1 if
85-
&lt;restart&gt; is less than &lt;step&gt;, and an error will be thrown
86-
if &lt;restart&gt; is equal to &lt;step&gt;. If provided with a boolean
83+
&lt;initial&gt; (or &lt;start&gt; if &lt;initial&gt; is not specified),
84+
and successive values will increase by &lt;step&gt;, wrapping around at
85+
&lt;restart&gt; back to &lt;start&gt;; &lt;step&gt; will default to 1 if
86+
&lt;restart&gt; is greater than &lt;start&gt;, will default to -1 if
87+
&lt;restart&gt; is less than &lt;start&gt;, and an error will be thrown
88+
if &lt;restart&gt; is equal to &lt;start&gt;. If provided with a boolean
8789
schema, only &lt;start&gt; may be specified; the resulting values will
8890
begin with &lt;start&gt; and alternate from `true` to `false` and from
89-
`false` to `true` from that point on.
91+
`false` to `true` from that point on.
9092
+ __range:__ A JSON object that conforms to the following format:
9193
`{"min": <min>, "max": <max>}` (at least one of "min" or "max" must be
9294
specified). If provided, ensures that the generated number will be

src/main/java/io/confluent/avro/random/generator/Generator.java

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,12 @@ public class Generator {
189189
* {@link #ITERATION_PROP_RESTART} is less than the value for {@link #ITERATION_PROP_START}.
190190
*/
191191
public static final String ITERATION_PROP_STEP = "step";
192+
/**
193+
* The name of the attribute for specifying the initial value in a schema with iterative
194+
* generation. If given, must be a numeric type that is integral if the given schema is as well.
195+
* If not given, defaults to the value for {@link #ITERATION_PROP_START}.
196+
*/
197+
public static final String ITERATION_PROP_INITIAL = "initial";
192198

193199
static final String DECIMAL_LOGICAL_TYPE_NAME = "decimal";
194200

@@ -650,6 +656,7 @@ private Iterator<Object> getIntegralIterator(
650656
Long iterationStartField,
651657
Long iterationRestartField,
652658
Long iterationStepField,
659+
Long iterationInitialField,
653660
IntegralIterator.Type type) {
654661

655662
if (iterationStartField == null) {
@@ -750,10 +757,16 @@ private Iterator<Object> getIntegralIterator(
750757
}
751758
}
752759

760+
long iterationInitial = iterationStart;
761+
if (iterationInitialField != null) {
762+
iterationInitial = iterationInitialField;
763+
}
764+
753765
return new IntegralIterator(
754766
iterationStart,
755767
iterationRestart,
756768
iterationStep,
769+
iterationInitial,
757770
generation,
758771
type
759772
);
@@ -763,6 +776,7 @@ private Iterator<Object> getDecimalIterator(
763776
Double iterationStartField,
764777
Double iterationRestartField,
765778
Double iterationStepField,
779+
Double iterationInitialField,
766780
DecimalIterator.Type type) {
767781

768782
if (iterationStartField == null) {
@@ -863,10 +877,16 @@ private Iterator<Object> getDecimalIterator(
863877
}
864878
}
865879

880+
double iterationInitial = iterationStart;
881+
if (iterationInitialField != null) {
882+
iterationInitial = iterationInitialField;
883+
}
884+
866885
return new DecimalIterator(
867886
iterationStart,
868887
iterationRestart,
869888
iterationStep,
889+
iterationInitial,
870890
generation,
871891
type
872892
);
@@ -927,10 +947,16 @@ private Iterator<Object> getDoubleIterator(final Map iterationProps) {
927947
ITERATION_PROP_STEP,
928948
iterationProps
929949
);
950+
Double iterationInitialField = getDecimalNumberField(
951+
ITERATION_PROP,
952+
ITERATION_PROP_INITIAL,
953+
iterationProps
954+
);
930955
return getDecimalIterator(
931956
iterationStartField,
932957
iterationRestartField,
933958
iterationStepField,
959+
iterationInitialField,
934960
DecimalIterator.Type.DOUBLE
935961
);
936962
}
@@ -951,10 +977,16 @@ private Iterator<Object> getFloatIterator(final Map iterationProps) {
951977
ITERATION_PROP_STEP,
952978
iterationProps
953979
);
980+
Float iterationInitialField = getFloatNumberField(
981+
ITERATION_PROP,
982+
ITERATION_PROP_INITIAL,
983+
iterationProps
984+
);
954985
return getDecimalIterator(
955986
iterationStartField != null ? iterationStartField.doubleValue() : null,
956987
iterationRestartField != null ? iterationRestartField.doubleValue() : null,
957988
iterationStepField != null ? iterationStepField.doubleValue() : null,
989+
iterationInitialField != null ? iterationInitialField.doubleValue() : null,
958990
DecimalIterator.Type.FLOAT
959991
);
960992
}
@@ -975,10 +1007,16 @@ private Iterator<Object> getLongIterator(final Map iterationProps) {
9751007
ITERATION_PROP_STEP,
9761008
iterationProps
9771009
);
1010+
Long iterationInitialField = getIntegralNumberField(
1011+
ITERATION_PROP,
1012+
ITERATION_PROP_INITIAL,
1013+
iterationProps
1014+
);
9781015
return getIntegralIterator(
9791016
iterationStartField,
9801017
iterationRestartField,
9811018
iterationStepField,
1019+
iterationInitialField,
9821020
IntegralIterator.Type.LONG
9831021
);
9841022
}
@@ -1013,10 +1051,16 @@ private Iterator<Object> getIntegerIterator(Map iterationProps) {
10131051
ITERATION_PROP_STEP,
10141052
iterationProps
10151053
);
1054+
Integer iterationInitialField = getIntegerNumberField(
1055+
ITERATION_PROP,
1056+
ITERATION_PROP_INITIAL,
1057+
iterationProps
1058+
);
10161059
return getIntegralIterator(
10171060
iterationStartField != null ? iterationStartField.longValue() : null,
10181061
iterationRestartField != null ? iterationRestartField.longValue() : null,
10191062
iterationStepField != null ? iterationStepField.longValue() : null,
1063+
iterationInitialField != null ? iterationInitialField.longValue() : null,
10201064
IntegralIterator.Type.INTEGER
10211065
);
10221066
}
@@ -1486,18 +1530,19 @@ public enum Type {
14861530
private final Type type;
14871531
private BigInteger current;
14881532

1489-
public IntegralIterator(long start, long restart, long step, long count, Type type) {
1533+
public IntegralIterator(long start, long restart, long step, long initial, long count, Type type) {
14901534
this.start = BigInteger.valueOf(start);
14911535
this.restart = BigInteger.valueOf(restart);
14921536
this.step = BigInteger.valueOf(step);
14931537
this.type = type;
1494-
current = BigInteger.ZERO;
1538+
current = BigInteger.valueOf(initial).subtract(this.start);
14951539
if (count > 0) {
14961540
// This is essentially the following expression when ignoring negative values:
14971541
// current = (count * step) % (restart - start)
14981542
// except BigInteger::mod only operates on positive numbers, so remove and re-add the sign after the modulo.
14991543
current = BigInteger.valueOf(count)
15001544
.multiply(this.step)
1545+
.add(current)
15011546
.abs()
15021547
.mod(this.restart.subtract(this.start).abs())
15031548
.multiply(this.step.divide(this.step.abs()));
@@ -1541,16 +1586,17 @@ public enum Type {
15411586
private final Type type;
15421587
private BigDecimal current;
15431588

1544-
public DecimalIterator(double start, double restart, double step, long count, Type type) {
1589+
public DecimalIterator(double start, double restart, double step, double initial, long count, Type type) {
15451590
this.start = BigDecimal.valueOf(start);
15461591
this.restart = BigDecimal.valueOf(restart);
15471592
this.modulo = this.restart.subtract(this.start);
15481593
this.step = BigDecimal.valueOf(step);
15491594
this.type = type;
1550-
current = BigDecimal.ZERO;
1595+
current = BigDecimal.valueOf(initial).subtract(this.start);
15511596
if (count > 0) {
15521597
current = BigDecimal.valueOf(count)
15531598
.multiply(this.step)
1599+
.add(current)
15541600
.remainder(this.modulo);
15551601
}
15561602
}

src/test/java/io/confluent/avro/random/generator/IterationTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@ public void shouldCreateIteratorPerFieldEvenIfSameSchema() {
3535
assertThat(((GenericRecord) second.get("nested")).get("long_iteration"), is(-3L));
3636
}
3737

38+
@Test
39+
public void shouldBeginIterationAtInitialValue() {
40+
final GenericRecord generated = (GenericRecord) generator.generate();
41+
assertThat(generated.get("int_iteration_offset"), is(40));
42+
assertThat(generated.get("long_iteration_offset"), is(0L));
43+
assertThat(generated.get("float_iteration_offset"), is(0.0f));
44+
assertThat(generated.get("double_iteration_offset"), is(5.0));
45+
}
46+
3847
@Test
3948
public void shouldSupportStringIteration() {
4049
final GenericRecord first = (GenericRecord) generator.generate();

src/test/resources/test-schemas/iteration.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@
2626
}
2727
}
2828
},
29+
{
30+
"name": "int_iteration_offset",
31+
"type": {
32+
"type": "int",
33+
"arg.properties": {
34+
"iteration": {
35+
"start": 50,
36+
"restart": -10,
37+
"initial": 40
38+
}
39+
}
40+
}
41+
},
2942
{
3043
"name": "long_iteration",
3144
"type": {
@@ -39,6 +52,20 @@
3952
}
4053
}
4154
},
55+
{
56+
"name": "long_iteration_offset",
57+
"type": {
58+
"type": "long",
59+
"arg.properties": {
60+
"iteration": {
61+
"start": -50,
62+
"restart": 100,
63+
"step": 47,
64+
"initial": 0
65+
}
66+
}
67+
}
68+
},
4269
{
4370
"name": "float_iteration",
4471
"type": {
@@ -51,6 +78,19 @@
5178
}
5279
}
5380
},
81+
{
82+
"name": "float_iteration_offset",
83+
"type": {
84+
"type":"float",
85+
"arg.properties": {
86+
"iteration": {
87+
"start": 10.4,
88+
"step": 5.1,
89+
"initial": 0
90+
}
91+
}
92+
}
93+
},
5494
{
5595
"name": "double_iteration",
5696
"type": {
@@ -64,6 +104,20 @@
64104
}
65105
}
66106
},
107+
{
108+
"name": "double_iteration_offset",
109+
"type": {
110+
"type": "double",
111+
"arg.properties": {
112+
"iteration": {
113+
"start": 0.0,
114+
"restart": 10.0,
115+
"step": 27.3,
116+
"initial": 5.0
117+
}
118+
}
119+
}
120+
},
67121
{
68122
"name": "string_iteration",
69123
"type": {

0 commit comments

Comments
 (0)