@Test
public void compareSetAndSetSizeInBits2() {
    EWAHCompressedBitmap32 bitmap1 = EWAHCompressedBitmap32.bitmapOf();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS * 3 / 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap32 bitmap2 = EWAHCompressedBitmap32.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap2.setSizeInBits(WORD_IN_BITS, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer, bitmap2.buffer);
}

@Test
public void compareSetAndSetSizeInBits2() {
    EWAHCompressedBitmap32 bitmap1 = EWAHCompressedBitmap32.bitmapOf();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS * 3 / 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap32 bitmap2 = EWAHCompressedBitmap32.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap2.setSizeInBits(WORD_IN_BITS, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer.getWords(), bitmap2.buffer.getWords());
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == (~0)) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            insertLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer.getLastWord() == 0) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.buffer.removeLastWord();
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            final int mask = ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            this.buffer.orLastWord(mask);
            if (this.buffer.getLastWord() == ~0) {
                this.buffer.removeLastWord();
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            insertLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            final int mask = ((~0) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            this.buffer.orLastWord(mask);
        }
        this.sizeInBits = size;
    }
    return true;
}

@Test
public void compareSetAndSetSizeInBits() {
    EWAHCompressedBitmap32 bitmap1 = EWAHCompressedBitmap32.bitmapOf();
    for (int i = WORD_IN_BITS / 2; i < WORD_IN_BITS; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS * 3 / 2; i < WORD_IN_BITS * 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap32 bitmap2 = EWAHCompressedBitmap32.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS, true);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer, bitmap2.buffer);
}

@Test
public void compareSetAndSetSizeInBits() {
    EWAHCompressedBitmap32 bitmap1 = EWAHCompressedBitmap32.bitmapOf();
    for (int i = WORD_IN_BITS / 2; i < WORD_IN_BITS; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS * 3 / 2; i < WORD_IN_BITS * 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap32 bitmap2 = EWAHCompressedBitmap32.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS, true);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer.getWords(), bitmap2.buffer.getWords());
}

@Test
public void testsetSizeInBits() {
    System.out.println("testing setSizeInBits");
    for (int k = 0; k < 4096; ++k) {
        EWAHCompressedBitmap32 ewah = new EWAHCompressedBitmap32();
        ewah.setSizeInBits(k, false);
        Assert.assertEquals(ewah.sizeInBits, k);
        Assert.assertEquals(ewah.cardinality(), 0);
        EWAHCompressedBitmap32 ewah2 = new EWAHCompressedBitmap32();
        ewah2.setSizeInBits(k, false);
        Assert.assertEquals(ewah2.sizeInBits, k);
        Assert.assertEquals(ewah2.cardinality(), 0);
        EWAHCompressedBitmap32 ewah3 = new EWAHCompressedBitmap32();
        for (int i = 0; i < k; ++i) {
            ewah3.set(i);
        }
        Assert.assertEquals(ewah3.sizeInBits, k);
        Assert.assertEquals(ewah3.cardinality(), k);
        EWAHCompressedBitmap32 ewah4 = new EWAHCompressedBitmap32();
        ewah4.setSizeInBits(k, true);
        Assert.assertEquals(ewah4.sizeInBits, k);
        Assert.assertEquals(ewah4.cardinality(), k);
    }
}

@Test
public void testsetSizeInBits() {
    System.out.println("testing setSizeInBits");
    for (int k = 0; k < 4096; ++k) {
        EWAHCompressedBitmap32 ewah = new EWAHCompressedBitmap32();
        ewah.setSizeInBits(k, false);
        Assert.assertEquals(ewah.sizeInBits(), k);
        Assert.assertEquals(ewah.cardinality(), 0);
        EWAHCompressedBitmap32 ewah2 = new EWAHCompressedBitmap32();
        ewah2.setSizeInBits(k, false);
        Assert.assertEquals(ewah2.sizeInBits(), k);
        Assert.assertEquals(ewah2.cardinality(), 0);
        EWAHCompressedBitmap32 ewah3 = new EWAHCompressedBitmap32();
        for (int i = 0; i < k; ++i) {
            ewah3.set(i);
        }
        Assert.assertEquals(ewah3.sizeInBits(), k);
        Assert.assertEquals(ewah3.cardinality(), k);
        EWAHCompressedBitmap32 ewah4 = new EWAHCompressedBitmap32();
        ewah4.setSizeInBits(k, true);
        Assert.assertEquals(ewah4.sizeInBits(), k);
        Assert.assertEquals(ewah4.cardinality(), k);
    }
}


@Test
public void testsetSizeInBits() {
    System.out.println("testing setSizeInBits");
    for (int k = 0; k < 4096; ++k) {
        EWAHCompressedBitmap ewah = new EWAHCompressedBitmap();
        ewah.setSizeInBits(k, false);
        Assert.assertEquals(ewah.sizeInBits, k);
        Assert.assertEquals(ewah.cardinality(), 0);
        EWAHCompressedBitmap ewah2 = new EWAHCompressedBitmap();
        ewah2.setSizeInBits(k, false);
        Assert.assertEquals(ewah2.sizeInBits, k);
        Assert.assertEquals(ewah2.cardinality(), 0);
        EWAHCompressedBitmap ewah3 = new EWAHCompressedBitmap();
        for (int i = 0; i < k; ++i) {
            ewah3.set(i);
        }
        Assert.assertEquals(ewah3.sizeInBits, k);
        Assert.assertEquals(ewah3.cardinality(), k);
        EWAHCompressedBitmap ewah4 = new EWAHCompressedBitmap();
        ewah4.setSizeInBits(k, true);
        Assert.assertEquals(ewah4.sizeInBits, k);
        Assert.assertEquals(ewah4.cardinality(), k);
    }
}

@Test
public void testsetSizeInBits() {
    System.out.println("testing setSizeInBits");
    for (int k = 0; k < 4096; ++k) {
        EWAHCompressedBitmap ewah = new EWAHCompressedBitmap();
        ewah.setSizeInBits(k, false);
        Assert.assertEquals(ewah.sizeInBits(), k);
        Assert.assertEquals(ewah.cardinality(), 0);
        EWAHCompressedBitmap ewah2 = new EWAHCompressedBitmap();
        ewah2.setSizeInBits(k, false);
        Assert.assertEquals(ewah2.sizeInBits(), k);
        Assert.assertEquals(ewah2.cardinality(), 0);
        EWAHCompressedBitmap ewah3 = new EWAHCompressedBitmap();
        for (int i = 0; i < k; ++i) {
            ewah3.set(i);
        }
        Assert.assertEquals(ewah3.sizeInBits(), k);
        Assert.assertEquals(ewah3.cardinality(), k);
        EWAHCompressedBitmap ewah4 = new EWAHCompressedBitmap();
        ewah4.setSizeInBits(k, true);
        Assert.assertEquals(ewah4.sizeInBits(), k);
        Assert.assertEquals(ewah4.cardinality(), k);
    }
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer[this.actualSizeInWords - 1] == 0l) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.actualSizeInWords--;
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            if (this.buffer[this.actualSizeInWords - 1] == ~0l) {
                this.buffer[this.actualSizeInWords - 1] = 0;
                --this.actualSizeInWords;
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            insertLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            this.buffer[this.actualSizeInWords - 1] |= ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
        }
        this.sizeInBits = size;
    }
    return true;
}

public boolean setSizeInBits(final int size, final boolean defaultValue) {
    if (size <= this.sizeInBits) {
        return false;
    }
    if ((this.sizeInBits % WORD_IN_BITS) != 0) {
        if (!defaultValue) {
            if (this.rlw.getNumberOfLiteralWords() > 0) {
                final int bitsToAdd = size - this.sizeInBits;
                final int usedBitsInLast = this.sizeInBits % WORD_IN_BITS;
                final int freeBitsInLast = WORD_IN_BITS - usedBitsInLast;
                if (this.buffer.getLastWord() == 0l) {
                    this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                    this.buffer.removeLastWord();
                    this.sizeInBits -= usedBitsInLast;
                } else if (usedBitsInLast > 0) {
                    this.sizeInBits += Math.min(bitsToAdd, freeBitsInLast);
                }
            }
        } else {
            if (this.rlw.getNumberOfLiteralWords() == 0) {
                this.rlw.setRunningLength(this.rlw.getRunningLength() - 1);
                insertLiteralWord(0);
            }
            final int maskWidth = Math.min(WORD_IN_BITS - this.sizeInBits % WORD_IN_BITS, size - this.sizeInBits);
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            final long mask = ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            this.buffer.orLastWord(mask);
            if (this.buffer.getLastWord() == ~0l) {
                this.buffer.removeLastWord();
                this.rlw.setNumberOfLiteralWords(this.rlw.getNumberOfLiteralWords() - 1);
                insertEmptyWord(true);
            }
            this.sizeInBits += maskWidth;
        }
    }
    this.addStreamOfEmptyWords(defaultValue, (size / WORD_IN_BITS) - (this.sizeInBits / WORD_IN_BITS));
    if (this.sizeInBits < size) {
        final int dist = distanceInWords(size - 1);
        if (dist > 0) {
            insertLiteralWord(0);
        }
        if (defaultValue) {
            final int maskWidth = size - this.sizeInBits;
            final int maskShift = this.sizeInBits % WORD_IN_BITS;
            final long mask = ((~0l) >>> (WORD_IN_BITS - maskWidth)) << maskShift;
            this.buffer.orLastWord(mask);
        }
        this.sizeInBits = size;
    }
    return true;
}

@Test
public void compareSetAndSetSizeInBits2() {
    EWAHCompressedBitmap bitmap1 = EWAHCompressedBitmap.bitmapOf();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS * 3 / 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap bitmap2 = EWAHCompressedBitmap.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap2.setSizeInBits(WORD_IN_BITS, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer, bitmap2.buffer);
}

@Test
public void compareSetAndSetSizeInBits2() {
    EWAHCompressedBitmap bitmap1 = EWAHCompressedBitmap.bitmapOf();
    for (int i = 0; i < WORD_IN_BITS / 2; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS; i < WORD_IN_BITS * 3 / 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap bitmap2 = EWAHCompressedBitmap.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, true);
    bitmap2.setSizeInBits(WORD_IN_BITS, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer.getWords(), bitmap2.buffer.getWords());
}

@Test
public void compareSetAndSetSizeInBits() {
    EWAHCompressedBitmap bitmap1 = EWAHCompressedBitmap.bitmapOf();
    for (int i = WORD_IN_BITS / 2; i < WORD_IN_BITS; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS * 3 / 2; i < WORD_IN_BITS * 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap bitmap2 = EWAHCompressedBitmap.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS, true);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer, bitmap2.buffer);
}

@Test
public void compareSetAndSetSizeInBits() {
    EWAHCompressedBitmap bitmap1 = EWAHCompressedBitmap.bitmapOf();
    for (int i = WORD_IN_BITS / 2; i < WORD_IN_BITS; ++i) {
        bitmap1.set(i);
    }
    for (int i = WORD_IN_BITS * 3 / 2; i < WORD_IN_BITS * 2; ++i) {
        bitmap1.set(i);
    }
    EWAHCompressedBitmap bitmap2 = EWAHCompressedBitmap.bitmapOf();
    bitmap2.setSizeInBits(WORD_IN_BITS / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS, true);
    bitmap2.setSizeInBits(WORD_IN_BITS * 3 / 2, false);
    bitmap2.setSizeInBits(WORD_IN_BITS * 2, true);
    Assert.assertEquals(bitmap1, bitmap2);
    Assert.assertArrayEquals(bitmap1.buffer.getWords(), bitmap2.buffer.getWords());
}


@Test
void assemblyHook() {
    List<Object> publishers = new ArrayList<>();
    Hooks.onEachOperator(objectPublisher -> {
        publishers.add(objectPublisher);
        return objectPublisher;
    });
    Iterator<Integer> items = createItems(5);
    Flux.fromIterable(() -> items).transform(OperatorUtils::discardOnCancel).as(StepVerifier::create).expectNextCount(5).verifyComplete();
    assertThat(publishers).hasSize(2).element(1).isExactlyInstanceOf(FluxDiscardOnCancel.class);
}

@Test
void assemblyHook() {
    List<Object> publishers = new ArrayList<>();
    Hooks.onEachOperator(objectPublisher -> {
        publishers.add(objectPublisher);
        return objectPublisher;
    });
    Iterator<Integer> items = createItems(5);
    Flux<Integer> flux = Flux.fromIterable(() -> items);
    flux.transform(OperatorUtils::discardOnCancel).as(StepVerifier::create).expectNextCount(5).verifyComplete();
    ObjectAssert<?> element = assertThat(publishers).hasSize(2).element(1);
    if (flux instanceof Fuseable) {
        element.isExactlyInstanceOf(FluxDiscardOnCancelFuseable.class);
    } else {
        element.isExactlyInstanceOf(FluxDiscardOnCancel.class);
    }
}

@Override
public void subscribe(CoreSubscriber<? super T> actual) {
    this.source.subscribe(new DiscardOnCancelFuseableSubscriber<>(actual));
}


@Test
default void stringify() {
    Codec<T> codec = getCodec(UnpooledByteBufAllocator.DEFAULT);
    T[] origin = originParameters();
    Object[] strings = stringifyParameters();
    assertEquals(origin.length, strings.length);
    for (int i = 0; i < origin.length; ++i) {
        ParameterWriter writer = ParameterWriterHelper.get(1);
        codec.encode(origin[i], CONTEXT).text(writer).as(StepVerifier::create).verifyComplete();
        assertEquals(ParameterWriterHelper.toSql(writer), strings[i].toString());
    }
}

@Test
default void stringify() {
    Codec<T> codec = getCodec(UnpooledByteBufAllocator.DEFAULT);
    T[] origin = originParameters();
    Object[] strings = stringifyParameters();
    assertEquals(origin.length, strings.length);
    for (int i = 0; i < origin.length; ++i) {
        ParameterWriter writer = ParameterWriterHelper.get(1);
        codec.encode(origin[i], CONTEXT).publishText(writer).as(StepVerifier::create).verifyComplete();
        assertEquals(ParameterWriterHelper.toSql(writer), strings[i].toString());
    }
}

@Override
public Mono<Void> text(ParameterWriter writer) {
    return Mono.fromRunnable(() -> encodeTo(writer));
}


@Test
void stringifySet() {
    SetCodec codec = getCodec(UnpooledByteBufAllocator.DEFAULT);
    String[] strings = stringifySets();
    assertEquals(sets.length, strings.length);
    for (int i = 0; i < sets.length; ++i) {
        ParameterWriter writer = ParameterWriterHelper.get(1);
        codec.encode(sets[i], CONTEXT).text(writer).as(StepVerifier::create).verifyComplete();
        assertEquals(ParameterWriterHelper.toSql(writer), strings[i]);
    }
}

@Test
void stringifySet() {
    SetCodec codec = getCodec(UnpooledByteBufAllocator.DEFAULT);
    String[] strings = stringifySets();
    assertEquals(sets.length, strings.length);
    for (int i = 0; i < sets.length; ++i) {
        ParameterWriter writer = ParameterWriterHelper.get(1);
        codec.encode(sets[i], CONTEXT).publishText(writer).as(StepVerifier::create).verifyComplete();
        assertEquals(ParameterWriterHelper.toSql(writer), strings[i]);
    }
}

@Override
public Mono<Void> text(ParameterWriter writer) {
    return Mono.fromRunnable(() -> encodeIterator(writer, new ConvertedIterator(value.iterator())));
}


@Test
void sslContextBuilderCustomizer() {
    String message = "Worked!";
    Function<SslContextBuilder, SslContextBuilder> customizer = ignored -> {
        throw new IllegalStateException(message);
    };
    MySqlConnectionConfiguration configuration = MySqlConnectionConfiguration.builder().host(HOST).username(USERNAME).sslMode(SslMode.REQUIRED).sslContextBuilderCustomizer(customizer).build();
    assertThatIllegalStateException().isThrownBy(() -> configuration.getSsl().customizeSslContext(SslContextBuilder.forClient())).withMessage(message);
}

@Test
void sslContextBuilderCustomizer() {
    String message = "Worked!";
    Function<SslContextBuilder, SslContextBuilder> customizer = ignored -> {
        throw new IllegalStateException(message);
    };
    MySqlConnectionConfiguration configuration = MySqlConnectionConfiguration.builder().host(HOST).user(USER).sslMode(SslMode.REQUIRED).sslContextBuilderCustomizer(customizer).build();
    assertThatIllegalStateException().isThrownBy(() -> configuration.getSsl().customizeSslContext(SslContextBuilder.forClient())).withMessage(message);
}

public Builder user(String user) {
    this.user = requireNonNull(user, "user must not be null");
    return this;
}

@Test
void autodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).username(USERNAME).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isNotEmpty();
}

@Test
void autodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).user(USER).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isNotEmpty();
}

public Builder username(String username) {
    this.username = requireNonNull(username, "username must not be null");
    return this;
}

public Builder username(String user) {
    return user(user);
}

@Test
void nonAutodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).username(USERNAME).autodetectExtensions(false).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isEmpty();
}

@Test
void nonAutodetectExtensions() {
    List<Extension> list = new ArrayList<>();
    MySqlConnectionConfiguration.builder().host(HOST).user(USER).autodetectExtensions(false).build().getExtensions().forEach(Extension.class, list::add);
    assertThat(list).isEmpty();
}


@Test
void dispose() {
    RequestQueue queue = new RequestQueue();
    List<Integer> arr = new AddEventList(queue);
    queue.submit(() -> arr.add(1));
    queue.submit(() -> arr.add(2));
    queue.dispose();
    assertThrows(IllegalStateException.class, () -> queue.submit(() -> arr.add(3)));
    assertThrows(IllegalStateException.class, () -> queue.submit(() -> arr.add(4)));
    assertEquals(arr, Arrays.asList(1, 2));
}

@Test
void dispose() {
    RequestQueue queue = new RequestQueue();
    List<Integer> arr = new AddEventList(queue);
    Mono.create(sink -> queue.submit(RequestTask.wrap(sink, () -> arr.add(5)))).as(StepVerifier::create).expectNext(true).verifyComplete();
    Mono.create(sink -> queue.submit(RequestTask.wrap(sink, () -> arr.add(4)))).as(StepVerifier::create).expectNext(true).verifyComplete();
    queue.dispose();
    Mono.create(sink -> queue.submit(RequestTask.wrap(sink, () -> arr.add(3)))).as(StepVerifier::create).verifyError(IllegalStateException.class);
    Mono.create(sink -> queue.submit(RequestTask.wrap(sink, () -> arr.add(2)))).as(StepVerifier::create).verifyError(IllegalStateException.class);
    assertEquals(arr, Arrays.asList(5, 4));
}

void dispose() {
    STATUS_UPDATER.set(this, DISPOSE);
    freeAll();
}

void dispose() {
    STATUS_UPDATER.set(this, DISPOSE);
    cancelAll(requireDisposed());
}


@Test
void toReadOnlyList() {
    Integer[] arr = new Integer[] { 1, 2, 3, 4 };
    ListAssert<Integer> listAssert = assertThat(InternalArrays.toReadOnlyList(arr)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    Arrays.fill(arr, 6);
    listAssert.isEqualTo(Arrays.asList(1, 2, 3, 4));
    assertThat(InternalArrays.toReadOnlyList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.toReadOnlyList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@Test
void toReadOnlyList() {
    Integer[] arr = new Integer[] { 1, 2, 3, 4 };
    ListAssert<Integer> listAssert = assertThat(InternalArrays.toImmutableList(arr)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    Arrays.fill(arr, 6);
    listAssert.isEqualTo(Arrays.asList(1, 2, 3, 4));
    assertThat(InternalArrays.toImmutableList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.toImmutableList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> toReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    switch(a.length) {
        case 0:
            return Collections.emptyList();
        case 1:
            return Collections.singletonList(a[0]);
        default:
            return new ArrList<>(Arrays.copyOf(a, a.length));
    }
}

@Test
void asReadOnlyList() {
    assertThat(InternalArrays.asReadOnlyList(1, 2, 3, 4)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    assertThat(InternalArrays.asReadOnlyList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.asReadOnlyList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@Test
void asReadOnlyList() {
    assertThat(InternalArrays.asImmutableList(1, 2, 3, 4)).isEqualTo(Arrays.asList(1, 2, 3, 4)).isNotInstanceOf(Arrays.asList(1, 2, 3, 4).getClass());
    assertThat(InternalArrays.asImmutableList(1)).isEqualTo(Collections.singletonList(1)).isExactlyInstanceOf(Collections.singletonList(1).getClass());
    assertThat(InternalArrays.asImmutableList()).isEqualTo(Collections.emptyList()).isExactlyInstanceOf(Collections.emptyList().getClass());
}

@SuppressWarnings("varargs")
@SafeVarargs
public static <E> List<E> asReadOnlyList(E... a) {
    requireNonNull(a, "array must not be null");
    switch(a.length) {
        case 0:
            return Collections.emptyList();
        case 1:
            return Collections.singletonList(a[0]);
        default:
            return new ArrList<>(a);
    }
}


@Test
public void addModuleInfoAndRunModular() throws Exception {
    prepareTestJar();
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    ProcessBuilder builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_RESOURCES + File.separator + "example.jar", "--module", "com.example").redirectOutput(Redirect.INHERIT);
    Process process = builder.start();
    process.waitFor();
    if (process.exitValue() == 0) {
        throw new AssertionError();
    }
    new AddModuleInfo("module com.example {}", "com.example.HelloWorld", "1.42.3", Paths.get("target", "generated-test-resources", "example.jar"), Paths.get("target", "generated-test-modules"), null, false).run();
    builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_MODULES + File.separator + "example.jar", "--module", "com.example");
    process = builder.start();
    process.waitFor();
    if (process.exitValue() != 0) {
        throw new AssertionError();
    }
}

@Test
public void addModuleInfoAndRunModular() throws Exception {
    prepareTestJar();
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    ProcessBuilder builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_RESOURCES + File.separator + "example.jar", "--module", "com.example").redirectOutput(Redirect.INHERIT);
    Process process = builder.start();
    process.waitFor();
    if (process.exitValue() == 0) {
        throw new AssertionError();
    }
    new AddModuleInfo("module com.example {}", "com.example.HelloWorld", "1.42.3", Paths.get("target", "generated-test-resources", "example.jar"), Paths.get("target", "generated-test-modules"), null, false, null).run();
    builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_MODULES + File.separator + "example.jar", "--module", "com.example");
    process = builder.start();
    process.waitFor();
    if (process.exitValue() != 0) {
        throw new AssertionError();
    }
}

public void run() {
    if (Files.isDirectory(inputJar)) {
        throw new IllegalArgumentException("Input JAR must not be a directory");
    }
    if (!Files.exists(outputDirectory)) {
        throw new IllegalArgumentException("Output directory doesn't exist: " + outputDirectory);
    }
    Path outputJar = outputDirectory.resolve(inputJar.getFileName());
    if (Files.exists(outputJar) && !overwriteExistingFiles) {
        throw new RuntimeException("File " + outputJar + " already exists; either set 'overwriteExistingFiles' to true or specify another output directory");
    }
    try {
        Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException e) {
        throw new RuntimeException("Couldn't copy JAR file", e);
    }
    ModuleDeclaration module = ModuleInfoCompiler.parseModuleInfo(moduleInfoSource);
    byte[] clazz = ModuleInfoCompiler.compileModuleInfo(module, mainClass, version);
    Map<String, String> env = new HashMap<>();
    env.put("create", "true");
    URI uri = URI.create("jar:" + outputJar.toUri().toString());
    try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
        if (jvmVersion == null) {
            Files.write(zipfs.getPath("module-info.class"), clazz, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
        } else {
            Path path = zipfs.getPath("META-INF/versions", jvmVersion.toString(), "module-info.class");
            Files.createDirectories(path.getParent());
            Files.write(path, clazz, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
            Path manifestPath = zipfs.getPath("META-INF/MANIFEST.MF");
            Manifest manifest;
            if (Files.exists(manifestPath)) {
                manifest = new Manifest(Files.newInputStream(manifestPath));
            } else {
                manifest = new Manifest();
                manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
            }
            manifest.getMainAttributes().put(new Attributes.Name("Multi-Release"), "true");
            try (OutputStream manifestOs = Files.newOutputStream(manifestPath, StandardOpenOption.TRUNCATE_EXISTING)) {
                manifest.write(manifestOs);
            }
        }
    } catch (IOException e) {
        throw new RuntimeException("Couldn't add module-info.class to JAR", e);
    }
}

public void run() {
    if (Files.isDirectory(inputJar)) {
        throw new IllegalArgumentException("Input JAR must not be a directory");
    }
    if (!Files.exists(outputDirectory)) {
        throw new IllegalArgumentException("Output directory doesn't exist: " + outputDirectory);
    }
    Path outputJar = outputDirectory.resolve(inputJar.getFileName());
    if (Files.exists(outputJar) && !overwriteExistingFiles) {
        throw new RuntimeException("File " + outputJar + " already exists; either set 'overwriteExistingFiles' to true or specify another output directory");
    }
    try {
        Files.copy(inputJar, outputJar, StandardCopyOption.REPLACE_EXISTING);
    } catch (IOException e) {
        throw new RuntimeException("Couldn't copy JAR file", e);
    }
    ModuleDeclaration module = ModuleInfoCompiler.parseModuleInfo(moduleInfoSource);
    byte[] clazz = ModuleInfoCompiler.compileModuleInfo(module, mainClass, version);
    Map<String, String> env = new HashMap<>();
    env.put("create", "true");
    URI uri = URI.create("jar:" + outputJar.toUri());
    try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) {
        if (jvmVersion == null) {
            Path path = zipfs.getPath("module-info.class");
            Files.write(path, clazz, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
            Files.setLastModifiedTime(path, toFileTime(timestamp));
        } else {
            Path path = zipfs.getPath("META-INF/versions", jvmVersion.toString(), "module-info.class");
            Files.createDirectories(path.getParent());
            Files.write(path, clazz, StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING);
            FileTime lastModifiedTime = toFileTime(timestamp);
            Files.setLastModifiedTime(path, lastModifiedTime);
            Files.setLastModifiedTime(path.getParent(), lastModifiedTime);
            Files.setLastModifiedTime(path.getParent().getParent(), lastModifiedTime);
            Path manifestPath = zipfs.getPath("META-INF/MANIFEST.MF");
            Manifest manifest;
            if (Files.exists(manifestPath)) {
                manifest = new Manifest(Files.newInputStream(manifestPath));
            } else {
                manifest = new Manifest();
                manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
            }
            manifest.getMainAttributes().put(new Attributes.Name("Multi-Release"), "true");
            try (OutputStream manifestOs = Files.newOutputStream(manifestPath, StandardOpenOption.TRUNCATE_EXISTING)) {
                manifest.write(manifestOs);
            }
            Files.setLastModifiedTime(manifestPath, lastModifiedTime);
        }
    } catch (IOException e) {
        throw new RuntimeException("Couldn't add module-info.class to JAR", e);
    }
}

@Test
public void addJvmVersionModuleInfoAndRunModular() throws Exception {
    prepareTestJar();
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    ProcessBuilder builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_RESOURCES + File.separator + "example.jar", "--module", "com.example").redirectOutput(Redirect.INHERIT);
    Process process = builder.start();
    process.waitFor();
    if (process.exitValue() == 0) {
        throw new AssertionError();
    }
    new AddModuleInfo("module com.example {}", "com.example.HelloWorld", "1.42.3", Paths.get("target", "generated-test-resources", "example.jar"), Paths.get("target", "generated-test-modules"), "9", false).run();
    builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_MODULES + File.separator + "example.jar", "--module", "com.example");
    process = builder.start();
    process.waitFor();
    if (process.exitValue() != 0) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        process.getInputStream().transferTo(baos);
        process.getErrorStream().transferTo(baos);
        throw new AssertionError(baos.toString());
    }
}

@Test
public void addJvmVersionModuleInfoAndRunModular() throws Exception {
    prepareTestJar();
    String javaHome = System.getProperty("java.home");
    String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
    ProcessBuilder builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_RESOURCES + File.separator + "example.jar", "--module", "com.example").redirectOutput(Redirect.INHERIT);
    Process process = builder.start();
    process.waitFor();
    if (process.exitValue() == 0) {
        throw new AssertionError();
    }
    new AddModuleInfo("module com.example {}", "com.example.HelloWorld", "1.42.3", Paths.get("target", "generated-test-resources", "example.jar"), Paths.get("target", "generated-test-modules"), "9", false, null).run();
    builder = new ProcessBuilder(javaBin, "--module-path", GENERATED_TEST_MODULES + File.separator + "example.jar", "--module", "com.example");
    process = builder.start();
    process.waitFor();
    if (process.exitValue() != 0) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        process.getInputStream().transferTo(baos);
        process.getErrorStream().transferTo(baos);
        throw new AssertionError(baos.toString());
    }
}


@Test
public void testBlankNumerics() throws Exception {
    File f = new File("src/test/resources/blank_cells.xlsx");
    try (StreamingReader reader = StreamingReader.builder().read(f)) {
        Row row = reader.iterator().next();
        assertThat(row.getCell(1).getStringCellValue(), is(nullValue()));
        assertThat(row.getCell(1).getDateCellValue(), is(nullValue()));
        assertThat(row.getCell(1).getNumericCellValue(), equalTo(0.0));
    }
}

@Test
public void testBlankNumerics() throws Exception {
    File f = new File("src/test/resources/blank_cells.xlsx");
    try (StreamingReader reader = StreamingReader.builder().read(f)) {
        Row row = reader.iterator().next();
        assertThat(row.getCell(1).getStringCellValue(), equalTo(""));
        assertThat(row.getCell(1).getDateCellValue(), is(nullValue()));
        assertThat(row.getCell(1).getNumericCellValue(), equalTo(0.0));
    }
}

@Override
public String getStringCellValue() {
    return (String) contents;
}

@Override
public String getStringCellValue() {
    return contents == null ? "" : (String) contents;
}


@Test
public void testStringsWithMultipleXmlElements() throws Exception {
    File file = new File("src/test/resources/blank_cells.xlsx");
    File sstCache = File.createTempFile("cache", ".sst");
    sstCache.deleteOnExit();
    try (OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ);
        BufferedStringsTable sst = BufferedStringsTable.getSharedStringsTable(sstCache, 1000, pkg)) {
        assertNotNull(sst);
        assertEquals("B1 is Blank --->", sst.getEntryAt(0).getT());
        assertEquals("B1 is Blank --->", sst.getItemAt(0).getString());
    }
}

@Test
public void testStringsWithMultipleXmlElements() throws Exception {
    File file = new File("src/test/resources/blank_cells.xlsx");
    File sstCache = File.createTempFile("cache", ".sst");
    sstCache.deleteOnExit();
    try (OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ);
        BufferedStringsTable sst = BufferedStringsTable.getSharedStringsTable(sstCache, 1000, pkg)) {
        assertNotNull(sst);
        assertEquals("B1 is Blank --->", sst.getEntryAt(0).getT());
    }
}

@Override
public RichTextString getItemAt(int idx) {
    return new XSSFRichTextString(list.getAt(idx));
}

@Test
public void testStringsWrappedInFormatting() throws Exception {
    File file = new File("src/test/resources/shared_styled_string.xlsx");
    File sstCache = File.createTempFile("cache", ".sst");
    sstCache.deleteOnExit();
    try (OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ);
        BufferedStringsTable sst = BufferedStringsTable.getSharedStringsTable(sstCache, 1000, pkg)) {
        assertNotNull(sst);
        assertEquals("shared styled string", sst.getItemAt(0).getString());
    }
}

@Test
public void testStringsWrappedInFormatting() throws Exception {
    File file = new File("src/test/resources/shared_styled_string.xlsx");
    File sstCache = File.createTempFile("cache", ".sst");
    sstCache.deleteOnExit();
    try (OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ);
        BufferedStringsTable sst = BufferedStringsTable.getSharedStringsTable(sstCache, 1000, pkg)) {
        assertNotNull(sst);
        assertEquals("shared styled string", sst.getEntryAt(0).getT());
    }
}


@Test
public void testSpecialStyles() throws Exception {
    File f = new File("src/test/resources/special_types.xlsx");
    Map<Integer, List<Cell>> contents = new HashMap<>();
    try (StreamingReader reader = StreamingReader.builder().read(f)) {
        for (Row row : reader) {
            contents.put(row.getRowNum(), new ArrayList<Cell>());
            for (Cell c : row) {
                if (c.getColumnIndex() > 0) {
                    contents.get(row.getRowNum()).add(c);
                }
            }
        }
    }
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
    assertThat(contents.size(), equalTo(2));
    assertThat(contents.get(1).size(), equalTo(4));
    assertThat(contents.get(1).get(0).getStringCellValue(), equalTo("Thu\", \"Dec 25\", \"14"));
    assertThat(contents.get(1).get(0).getDateCellValue(), equalTo(df.parse("25/12/2014")));
    assertThat(contents.get(1).get(1).getStringCellValue(), equalTo("02/04/15"));
    assertThat(contents.get(1).get(1).getDateCellValue(), equalTo(df.parse("04/02/2015")));
    assertThat(contents.get(1).get(2).getStringCellValue(), equalTo("14\". \"Mar\". \"2015"));
    assertThat(contents.get(1).get(2).getDateCellValue(), equalTo(df.parse("14/03/2015")));
    assertThat(contents.get(1).get(3).getStringCellValue(), equalTo("2015-05-05"));
    assertThat(contents.get(1).get(3).getDateCellValue(), equalTo(df.parse("05/05/2015")));
    assertThat(contents.get(2).size(), equalTo(4));
    assertThat(contents.get(2).get(0).getStringCellValue(), equalTo("3.12"));
    assertThat(contents.get(2).get(0).getNumericCellValue(), equalTo(3.12312312312));
    assertThat(contents.get(2).get(1).getStringCellValue(), equalTo("1,023,042"));
    assertThat(contents.get(2).get(1).getNumericCellValue(), equalTo(1023042.0));
    assertThat(contents.get(2).get(2).getStringCellValue(), equalTo("-312,231.12"));
    assertThat(contents.get(2).get(2).getNumericCellValue(), equalTo(-312231.12123145));
    assertThat(contents.get(2).get(3).getStringCellValue(), equalTo("(132)"));
    assertThat(contents.get(2).get(3).getNumericCellValue(), equalTo(-132.0));
}

@Test
public void testSpecialStyles() throws Exception {
    File f = new File("src/test/resources/special_types.xlsx");
    Map<Integer, List<Cell>> contents = new HashMap<>();
    try (StreamingReader reader = StreamingReader.builder().read(f)) {
        for (Row row : reader) {
            contents.put(row.getRowNum(), new ArrayList<Cell>());
            for (Cell c : row) {
                if (c.getColumnIndex() > 0) {
                    contents.get(row.getRowNum()).add(c);
                }
            }
        }
    }
    SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy");
    assertThat(contents.size(), equalTo(2));
    assertThat(contents.get(0).size(), equalTo(4));
    assertThat(contents.get(0).get(0).getStringCellValue(), equalTo("Thu\", \"Dec 25\", \"14"));
    assertThat(contents.get(0).get(0).getDateCellValue(), equalTo(df.parse("25/12/2014")));
    assertThat(contents.get(0).get(1).getStringCellValue(), equalTo("02/04/15"));
    assertThat(contents.get(0).get(1).getDateCellValue(), equalTo(df.parse("04/02/2015")));
    assertThat(contents.get(0).get(2).getStringCellValue(), equalTo("14\". \"Mar\". \"2015"));
    assertThat(contents.get(0).get(2).getDateCellValue(), equalTo(df.parse("14/03/2015")));
    assertThat(contents.get(0).get(3).getStringCellValue(), equalTo("2015-05-05"));
    assertThat(contents.get(0).get(3).getDateCellValue(), equalTo(df.parse("05/05/2015")));
    assertThat(contents.get(1).size(), equalTo(4));
    assertThat(contents.get(1).get(0).getStringCellValue(), equalTo("3.12"));
    assertThat(contents.get(1).get(0).getNumericCellValue(), equalTo(3.12312312312));
    assertThat(contents.get(1).get(1).getStringCellValue(), equalTo("1,023,042"));
    assertThat(contents.get(1).get(1).getNumericCellValue(), equalTo(1023042.0));
    assertThat(contents.get(1).get(2).getStringCellValue(), equalTo("-312,231.12"));
    assertThat(contents.get(1).get(2).getNumericCellValue(), equalTo(-312231.12123145));
    assertThat(contents.get(1).get(3).getStringCellValue(), equalTo("(132)"));
    assertThat(contents.get(1).get(3).getNumericCellValue(), equalTo(-132.0));
}

@Override
public Date getDateCellValue() {
    return HSSFDateUtil.getJavaDate(getNumericCellValue());
}

@Override
public Date getDateCellValue() {
    return rawContents == null ? null : HSSFDateUtil.getJavaDate(getNumericCellValue());
}


@Test
public void shouldRetrieveAccessTokenAndUpdateConsumer() throws Exception {
    provider.retrieveAccessToken(null);
    verify(consumerMock).sign((HttpRequest) anyObject());
    verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET);
    verify(connectionMock).disconnect();
}

@Test
public void shouldRetrieveAccessTokenAndUpdateConsumer() throws Exception {
    provider.retrieveAccessToken(null);
    verify(consumerMock).sign((HttpRequest) anyObject());
    verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET);
}

public void retrieveAccessToken(String oauthVerifier) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException {
    if (consumer.getToken() == null || consumer.getTokenSecret() == null) {
        throw new OAuthExpectationFailedException("Authorized request token or token secret not set. " + "Did you retrieve an authorized request token before?");
    }
    retrieveToken(isOAuth10a && oauthVerifier != null ? OAuth.addQueryParameters(accessTokenEndpointUrl, OAuth.OAUTH_VERIFIER, oauthVerifier) : accessTokenEndpointUrl);
}

@Test
public void shouldRetrieveRequestTokenAndUpdateConsumer() throws Exception {
    String callbackUrl = "http://www.example.com";
    String result = provider.retrieveRequestToken(callbackUrl);
    verify(consumerMock).sign((HttpRequest) anyObject());
    verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET);
    verify(connectionMock).disconnect();
    assertEquals(AUTHORIZE_WEBSITE_URL + "?" + OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_CALLBACK + "=" + "http%3A%2F%2Fwww.example.com", result);
}

@Test
public void shouldRetrieveRequestTokenAndUpdateConsumer() throws Exception {
    String callbackUrl = "http://www.example.com";
    String result = provider.retrieveRequestToken(callbackUrl);
    verify(consumerMock).sign((HttpRequest) anyObject());
    verify(consumerMock).setTokenWithSecret(TOKEN, TOKEN_SECRET);
    assertEquals(AUTHORIZE_WEBSITE_URL + "?" + OAuth.OAUTH_TOKEN + "=" + TOKEN + "&" + OAuth.OAUTH_CALLBACK + "=" + "http%3A%2F%2Fwww.example.com", result);
}

public String retrieveRequestToken(String callbackUrl) throws OAuthMessageSignerException, OAuthNotAuthorizedException, OAuthExpectationFailedException, OAuthCommunicationException {
    consumer.setTokenWithSecret(null, null);
    retrieveToken(OAuth.addQueryParameters(requestTokenEndpointUrl, OAuth.OAUTH_CALLBACK, callbackUrl));
    String callbackConfirmed = responseParameters.get(OAuth.OAUTH_CALLBACK_CONFIRMED);
    responseParameters.remove(OAuth.OAUTH_CALLBACK_CONFIRMED);
    isOAuth10a = Boolean.TRUE.toString().equals(callbackConfirmed);
    if (isOAuth10a) {
        return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN, consumer.getToken());
    } else {
        return OAuth.addQueryParameters(authorizationWebsiteUrl, OAuth.OAUTH_TOKEN, consumer.getToken(), OAuth.OAUTH_CALLBACK, callbackUrl);
    }
}


@Test
void walkValidateThen() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkListenerRunner(keywordWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"false\"", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertFalse(result.getErrors().isEmpty());
    List<WalkEvent> types = result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(1, types.size());
    assertEquals("", types.get(0).getInstanceLocation().toString());
    assertEquals("/then", types.get(0).getEvaluationPath().toString());
}

@Test
void walkValidateThen() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkHandler keywordWalkHandler = KeywordWalkHandler.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkHandler(keywordWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"false\"", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertFalse(result.getErrors().isEmpty());
    List<WalkEvent> types = result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(1, types.size());
    assertEquals("", types.get(0).getInstanceLocation().toString());
    assertEquals("/then", types.get(0).getEvaluationPath().toString());
}

public Builder keywordWalkListenerRunner(WalkListenerRunner keywordWalkListenerRunner) {
    this.keywordWalkListenerRunner = keywordWalkListenerRunner;
    return this;
}

@Test
void walkValidateElse() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkListenerRunner(keywordWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"hello\"", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertFalse(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(1, types.size());
    assertEquals("", types.get(0).getInstanceLocation().toString());
    assertEquals("/else", types.get(0).getEvaluationPath().toString());
}

@Test
void walkValidateElse() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkHandler keywordWalkHandler = KeywordWalkHandler.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkHandler(keywordWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"hello\"", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertFalse(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(1, types.size());
    assertEquals("", types.get(0).getInstanceLocation().toString());
    assertEquals("/else", types.get(0).getEvaluationPath().toString());
}

@Test
void walkNoValidate() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkListenerRunner(keywordWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"false\"", InputFormat.JSON, false, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(2, types.size());
}

@Test
void walkNoValidate() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkHandler keywordWalkHandler = KeywordWalkHandler.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkHandler(keywordWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("\"false\"", InputFormat.JSON, false, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(2, types.size());
}

public Builder keywordWalkHandler(WalkHandler keywordWalkHandler) {
    this.keywordWalkHandler = keywordWalkHandler;
    return this;
}

@Test
void walkValidateNull() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkListenerRunner(keywordWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(2, types.size());
}

@Test
void walkValidateNull() {
    String schemaData = "{\r\n" + "  \"if\": {\r\n" + "    \"const\": \"false\"\r\n" + "  },\r\n" + "  \"then\": {\r\n" + "    \"type\": \"object\"\r\n" + "  },\r\n" + "  \"else\": {\r\n" + "    \"type\": \"number\"\r\n" + "  }\r\n" + "}";
    KeywordWalkHandler keywordWalkHandler = KeywordWalkHandler.builder().keywordWalkListener(KeywordType.TYPE.getValue(), new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> types = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("types", key -> new ArrayList<NodePath>());
            types.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkHandler(keywordWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> types = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("types");
    assertEquals(2, types.size());
}


@Test
void test() throws JsonProcessingException {
    StringCollector stringCollector = new StringCollector();
    KeywordWalkListenerRunner keywordWalkListenerRunner = KeywordWalkListenerRunner.builder().keywordWalkListener(stringCollector).build();
    String schema = "{\n" + "  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + "  \"type\" : \"object\",\n" + "  \"properties\" : {\n" + "    \"credit_card\": {\n" + "      \"type\" : \"string\"\n" + "    }\n" + "  },\n" + "  \"dependentSchemas\": {\n" + "    \"credit_card\": {\n" + "      \"properties\": {\n" + "        \"billing_address\": {\n" + "          \"type\" : \"string\"\n" + "        }\n" + "      },\n" + "      \"required\": [\"billing_address\"]\n" + "    }\n" + "  }\n" + "}\n";
    String data = "{\n" + "  \"credit_card\" : \"my_credit_card\",\n" + "  \"billing_address\" : \"my_billing_address\"\n" + "}\n";
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkListenerRunner(keywordWalkListenerRunner).build();
    Schema jsonSchema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12).getSchema(schema);
    jsonSchema.walk(new ObjectMapper().readTree(data), false, executionContext -> executionContext.setWalkConfig(walkConfig));
    System.out.println(stringCollector.strings);
    assertLinesMatch(Arrays.asList("my_credit_card", "my_billing_address"), stringCollector.strings);
}

@Test
void test() throws JsonProcessingException {
    StringCollector stringCollector = new StringCollector();
    KeywordWalkHandler keywordWalkHandler = KeywordWalkHandler.builder().keywordWalkListener(stringCollector).build();
    String schema = "{\n" + "  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n" + "  \"type\" : \"object\",\n" + "  \"properties\" : {\n" + "    \"credit_card\": {\n" + "      \"type\" : \"string\"\n" + "    }\n" + "  },\n" + "  \"dependentSchemas\": {\n" + "    \"credit_card\": {\n" + "      \"properties\": {\n" + "        \"billing_address\": {\n" + "          \"type\" : \"string\"\n" + "        }\n" + "      },\n" + "      \"required\": [\"billing_address\"]\n" + "    }\n" + "  }\n" + "}\n";
    String data = "{\n" + "  \"credit_card\" : \"my_credit_card\",\n" + "  \"billing_address\" : \"my_billing_address\"\n" + "}\n";
    WalkConfig walkConfig = WalkConfig.builder().keywordWalkHandler(keywordWalkHandler).build();
    Schema jsonSchema = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12).getSchema(schema);
    jsonSchema.walk(new ObjectMapper().readTree(data), false, executionContext -> executionContext.setWalkConfig(walkConfig));
    System.out.println(stringCollector.strings);
    assertLinesMatch(Arrays.asList("my_credit_card", "my_billing_address"), stringCollector.strings);
}


@Test
void walk() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("[\"the\",\"quick\",\"brown\"]", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
}

@Test
void walk() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk("[\"the\",\"quick\",\"brown\"]", InputFormat.JSON, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
}

public Builder itemWalkHandler(WalkHandler itemWalkHandler) {
    this.itemWalkHandler = itemWalkHandler;
    return this;
}

@Test
void walkNull() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(1, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
}

@Test
void walkNull() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(1, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
}

@Test
void walkTupleItemsAdditionalDefaults() throws JsonProcessingException {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\",\r\n" + "      \"default\": \"1\"\r\n" + "    },\r\n" + "    {\r\n" + "      \"type\": \"integer\",\r\n" + "      \"default\": 2\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\",\r\n" + "    \"default\": \"additional\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null, null, null]");
    Result result = schema.walk(input, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(4, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertEquals("1", items.get(0).getInstanceNode().textValue());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertEquals(2, items.get(1).getInstanceNode().intValue());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertEquals("additional", items.get(2).getInstanceNode().asText());
    assertEquals("/3", items.get(3).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(3).getKeyword());
    assertEquals("additional", items.get(3).getInstanceNode().asText());
}

@Test
void walkTupleItemsAdditionalDefaults() throws JsonProcessingException {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\",\r\n" + "      \"default\": \"1\"\r\n" + "    },\r\n" + "    {\r\n" + "      \"type\": \"integer\",\r\n" + "      \"default\": 2\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\",\r\n" + "    \"default\": \"additional\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).applyDefaultsStrategy(new ApplyDefaultsStrategy(true, true, true)).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    JsonNode input = JsonMapperFactory.getInstance().readTree("[null, null, null, null]");
    Result result = schema.walk(input, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(4, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertEquals("1", items.get(0).getInstanceNode().textValue());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertEquals(2, items.get(1).getInstanceNode().intValue());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertEquals("additional", items.get(2).getInstanceNode().asText());
    assertEquals("/3", items.get(3).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(3).getKeyword());
    assertEquals("additional", items.get(3).getInstanceNode().asText());
}

public ItemWalkListenerRunner build() {
    return new ItemWalkListenerRunner(itemWalkListeners);
}

@Test
void walkNullTupleItemsAdditional() {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\"\r\n" + "    }\r\n," + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertNull(items.get(0).getInstanceNode());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertNull(items.get(1).getInstanceNode());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertNull(items.get(2).getInstanceNode());
}

@Test
void walkNullTupleItemsAdditional() {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\"\r\n" + "    }\r\n," + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertNull(items.get(0).getInstanceNode());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertNull(items.get(1).getInstanceNode());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertNull(items.get(2).getInstanceNode());
}

@Test
void walkTupleItemsAdditional() throws JsonProcessingException {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\"\r\n" + "    }\r\n," + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    JsonNode input = JsonMapperFactory.getInstance().readTree("[\"hello\"]");
    Result result = schema.walk(input, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertEquals("hello", items.get(0).getInstanceNode().textValue());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertNull(items.get(1).getInstanceNode());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertNull(items.get(2).getInstanceNode());
}

@Test
void walkTupleItemsAdditional() throws JsonProcessingException {
    String schemaData = "{\r\n" + "  \"items\": [\r\n" + "    {\r\n" + "      \"type\": \"string\"\r\n" + "    }\r\n," + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"additionalItems\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2019_09);
    Schema schema = factory.getSchema(schemaData);
    JsonNode input = JsonMapperFactory.getInstance().readTree("[\"hello\"]");
    Result result = schema.walk(input, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(3, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("items", items.get(0).getKeyword());
    assertEquals("hello", items.get(0).getInstanceNode().textValue());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
    assertNull(items.get(1).getInstanceNode());
    assertEquals("/2", items.get(2).getInstanceLocation().toString());
    assertEquals("additionalItems", items.get(2).getKeyword());
    assertNull(items.get(2).getInstanceNode());
}


@Test
void walkNullPrefixItems() {
    String schemaData = "{\r\n" + "  \"prefixItems\": [\r\n" + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(2, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("prefixItems", items.get(0).getKeyword());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
}

@Test
void walkNullPrefixItems() {
    String schemaData = "{\r\n" + "  \"prefixItems\": [\r\n" + "    {\r\n" + "      \"type\": \"integer\"\r\n" + "    }\r\n" + "  ],\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(2, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
    assertEquals("prefixItems", items.get(0).getKeyword());
    assertEquals("/1", items.get(1).getInstanceLocation().toString());
    assertEquals("items", items.get(1).getKeyword());
}

public Builder itemWalkListenerRunner(WalkListenerRunner itemWalkListenerRunner) {
    this.itemWalkListenerRunner = itemWalkListenerRunner;
    return this;
}

@Test
void walkNull() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkListenerRunner itemWalkListenerRunner = ItemWalkListenerRunner.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkListenerRunner(itemWalkListenerRunner).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(1, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
}

@Test
void walkNull() {
    String schemaData = "{\r\n" + "  \"items\": {\r\n" + "    \"type\": \"string\"\r\n" + "  }\r\n" + "}";
    ItemWalkHandler itemWalkHandler = ItemWalkHandler.builder().itemWalkListener(new WalkListener() {

        @Override
        public WalkFlow onWalkStart(WalkEvent walkEvent) {
            return WalkFlow.CONTINUE;
        }

        @Override
        public void onWalkEnd(WalkEvent walkEvent, List<Error> errors) {
            @SuppressWarnings("unchecked")
            List<WalkEvent> items = (List<WalkEvent>) walkEvent.getExecutionContext().getCollectorContext().getData().computeIfAbsent("items", key -> new ArrayList<NodePath>());
            items.add(walkEvent);
        }
    }).build();
    WalkConfig walkConfig = WalkConfig.builder().itemWalkHandler(itemWalkHandler).build();
    SchemaRegistry factory = SchemaRegistry.withDefaultDialect(SpecificationVersion.DRAFT_2020_12);
    Schema schema = factory.getSchema(schemaData);
    Result result = schema.walk(null, true, executionContext -> executionContext.setWalkConfig(walkConfig));
    assertTrue(result.getErrors().isEmpty());
    @SuppressWarnings("unchecked")
    List<WalkEvent> items = (List<WalkEvent>) result.getExecutionContext().getCollectorContext().get("items");
    assertEquals(1, items.size());
    assertEquals("/0", items.get(0).getInstanceLocation().toString());
}