Skip to content

Commit

Permalink
Loading FIP layer data file
Browse files Browse the repository at this point in the history
  • Loading branch information
smithkm committed Jul 6, 2023
1 parent f4a615a commit 98cb956
Show file tree
Hide file tree
Showing 13 changed files with 959 additions and 58 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ca.bc.gov.nrs.vdyp.io;

public enum EndOfRecord {
END_OF_RECORD
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package ca.bc.gov.nrs.vdyp.io.parse;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
* Wraps a StreamingParser and groups its entries.
*
* @author Kevin Smith, Vivid Solutions
*
* @param <T>
* @param <U>
*/
public abstract class GroupingStreamingParser<T, U> implements StreamingParser<T> {

final StreamingParser<U> delegate;

Optional<List<U>> next = Optional.empty();

public GroupingStreamingParser(StreamingParser<U> delegate) {
super();
this.delegate = delegate;
}

/**
* Returns true if a child should be skipped
*
* @param nextChild
* @return
*/
protected abstract boolean skip(U nextChild);

/**
* Returns true if a schild is an end of group marker
*
* @param nextChild
* @return
*/
protected abstract boolean stop(U nextChild);

/**
* Convert a list of children to a result.
*
* @param children
* @return
*/
protected abstract T convert(List<U> children);

@Override
public T next() throws IOException, ResourceParseException {

doGetNext();
var children = next.orElseThrow(() -> new IllegalStateException("Requested next group when there is none"));
next = Optional.empty();
return convert(children);
}

protected void doGetNext() throws IOException, ResourceParseException {

if (next.isEmpty()) {
var nextResult = new ArrayList<U>();

var nextChild = safeNextChild();
while (nextChild.map(x -> !stop(x)).orElse(false)) {
nextResult.add(nextChild.get());
nextChild = safeNextChild();
}
if (nextChild.isEmpty()) {
return;
}
next = Optional.of(nextResult);

}
}

protected Optional<U> safeNextChild() throws IOException, ResourceParseException {
Optional<U> result = Optional.empty();
while (result.isEmpty() && delegate.hasNext()) {
if (delegate.hasNext())
result = Optional.of(delegate.next()).filter(x -> !skip(x));
}
return result;
}

@Override
public boolean hasNext() throws IOException, ResourceParseException {
doGetNext();

return next.isPresent();
}

@Override
public void close() throws IOException {
delegate.close();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;

import ca.bc.gov.nrs.vdyp.common.ValueOrMarker;
import ca.bc.gov.nrs.vdyp.model.Layer;
import ca.bc.gov.nrs.vdyp.model.Region;

/**
Expand Down Expand Up @@ -205,4 +208,57 @@ public static <U> ValueParser<List<U>> segmentList(int length, ValueParser<U> de
static <U> ValueParser<U> uncontrolled(ControlledValueParser<U> delegate) {
return s -> delegate.parse(s, Collections.emptyMap());
}

/**
* Attempt to parse as a marker using markerParser. If it returns empty, parse
* as a value with valueParser.
*
* @param valueParser Parser from String to Value.
* @param markerParser Parser from String to Optional<Marker>. This should
* return empty if the string is not a marker.
* @return a ValueOrMarker
*/
public static <Value, Marker> ValueParser<ValueOrMarker<Value, Marker>>
valueOrMarker(ValueParser<Value> valueParser, ValueParser<Optional<Marker>> markerParser) {
return s -> {
var builder = new ValueOrMarker.Builder<Value, Marker>();
var marker = markerParser.parse(s).map(builder::marker);
if (marker.isPresent()) {
return marker.get();
}

var value = builder.value(valueParser.parse(s));
return value;
};
}

/**
* Return the given value if the test passes, otherwise empty.
*
* @param <T>
* @param test
* @param value
* @return
*/
public static <T> ValueParser<Optional<T>> optionalSingleton(Predicate<String> test, T value) {
return s -> test.test(s) ? Optional.of(value) : Optional.empty();
}

/**
* Parser for a layer identifier
*/
public static ValueParser<Optional<Layer>> LAYER = s -> {
switch (s.toUpperCase()) {
case "1":
case "P":
return Optional.of(Layer.PRIMARY);
case "2":
case "S":
return Optional.of(Layer.SECONDARY);
case "V":
return Optional.of(Layer.VETERAN);
default:
return Optional.empty(); // Unknown
}
};
}
5 changes: 5 additions & 0 deletions vdyp-core/src/main/java/ca/bc/gov/nrs/vdyp/model/Layer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ca.bc.gov.nrs.vdyp.model;

public enum Layer {
PRIMARY, SECONDARY, VETERAN
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package ca.bc.gov.nrs.vdyp.io.parse;

import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.assertEmpty;
import static ca.bc.gov.nrs.vdyp.test.VdypMatchers.assertNext;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;

import java.util.Arrays;
import java.util.List;

import org.easymock.EasyMock;
import org.junit.jupiter.api.Test;

public class GroupingStreamingParserTest {

@Test
public void testEmpty() throws Exception {
var control = EasyMock.createControl();

StreamingParser<Integer> delegate = control.createMock("delegate", StreamingParser.class);

EasyMock.expect(delegate.hasNext()).andStubReturn(false);

control.replay();

var unit = new GroupingStreamingParser<List<Integer>, Integer>(delegate) {

@Override
protected boolean skip(Integer nextChild) {
return nextChild % 3 == 0;
}

@Override
protected boolean stop(Integer nextChild) {
return nextChild % 5 == 0;
}

@Override
protected List<Integer> convert(List<Integer> children) {
return children;
}

};

assertEmpty(unit);

control.verify();
}

@Test
public void testOneEntry() throws Exception {
var control = EasyMock.createControl();

var mock = Arrays.asList(1, 5).iterator();

StreamingParser<Integer> delegate = control.createMock("delegate", StreamingParser.class);

EasyMock.expect(delegate.hasNext()).andStubAnswer(mock::hasNext);
EasyMock.expect(delegate.next()).andStubAnswer(mock::next);

control.replay();

var unit = new GroupingStreamingParser<List<Integer>, Integer>(delegate) {

@Override
protected boolean skip(Integer nextChild) {
return nextChild % 3 == 0;
}

@Override
protected boolean stop(Integer nextChild) {
return nextChild % 5 == 0;
}

@Override
protected List<Integer> convert(List<Integer> children) {
return children;
}

};

var group = assertNext(unit);
assertThat(group, contains(1));

assertEmpty(unit);

control.verify();
}

@Test
public void testMultipleEntries() throws Exception {
var control = EasyMock.createControl();

var mock = Arrays.asList(1, 2, 5).iterator();

StreamingParser<Integer> delegate = control.createMock("delegate", StreamingParser.class);

EasyMock.expect(delegate.hasNext()).andStubAnswer(mock::hasNext);
EasyMock.expect(delegate.next()).andStubAnswer(mock::next);

control.replay();

var unit = new GroupingStreamingParser<List<Integer>, Integer>(delegate) {

@Override
protected boolean skip(Integer nextChild) {
return nextChild % 3 == 0;
}

@Override
protected boolean stop(Integer nextChild) {
return nextChild % 5 == 0;
}

@Override
protected List<Integer> convert(List<Integer> children) {
return children;
}

};

var group = assertNext(unit);
assertThat(group, contains(1, 2));

assertEmpty(unit);

control.verify();

}

@Test
public void testMultipleGroups() throws Exception {
var control = EasyMock.createControl();

var mock = Arrays.asList(1, 5, 2, 5).iterator();

StreamingParser<Integer> delegate = control.createMock("delegate", StreamingParser.class);

EasyMock.expect(delegate.hasNext()).andStubAnswer(mock::hasNext);
EasyMock.expect(delegate.next()).andStubAnswer(mock::next);

control.replay();

var unit = new GroupingStreamingParser<List<Integer>, Integer>(delegate) {

@Override
protected boolean skip(Integer nextChild) {
return nextChild % 3 == 0;
}

@Override
protected boolean stop(Integer nextChild) {
return nextChild % 5 == 0;
}

@Override
protected List<Integer> convert(List<Integer> children) {
return children;
}

};

var group = assertNext(unit);
assertThat(group, contains(1));

group = assertNext(unit);
assertThat(group, contains(2));

assertEmpty(unit);

control.verify();

}

@Test
public void testSkip() throws Exception {
var control = EasyMock.createControl();

var mock = Arrays.asList(3, 1, 5).iterator();

StreamingParser<Integer> delegate = control.createMock("delegate", StreamingParser.class);

EasyMock.expect(delegate.hasNext()).andStubAnswer(mock::hasNext);
EasyMock.expect(delegate.next()).andStubAnswer(mock::next);

control.replay();

var unit = new GroupingStreamingParser<List<Integer>, Integer>(delegate) {

@Override
protected boolean skip(Integer nextChild) {
return nextChild % 3 == 0;
}

@Override
protected boolean stop(Integer nextChild) {
return nextChild % 5 == 0;
}

@Override
protected List<Integer> convert(List<Integer> children) {
return children;
}

};

var group = assertNext(unit);
assertThat(group, contains(1));

assertEmpty(unit);

control.verify();
}

}
Loading

0 comments on commit 98cb956

Please sign in to comment.