Skip to content

Resolve sonar code smells #273

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Dec 27, 2018
15 changes: 10 additions & 5 deletions core/src/main/java/io/parsingdata/metal/Util.java
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@

import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.util.Locale;
import java.util.Optional;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
@@ -33,13 +34,13 @@

public final class Util {

final private static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); // Private because array content is mutable.
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); // Private because array content is mutable.

private Util() {}

public static <T>T checkNotNull(final T argument, final String name) {
if (argument == null) {
throw new IllegalArgumentException("Argument " + name + " may not be null.");
throw new IllegalArgumentException(format("Argument %s may not be null.", name));
}
return argument;
}
@@ -48,15 +49,15 @@ public static <T>T[] checkContainsNoNulls(final T[] arguments, final String name
checkNotNull(arguments, name);
for (final T argument : arguments) {
if (argument == null) {
throw new IllegalArgumentException("Value in array " + name + " may not be null.");
throw new IllegalArgumentException(format("Value in array %s may not be null.", name));
}
}
return arguments;
}

public static String checkNotEmpty(final String argument, final String name) {
if (checkNotNull(argument, name).isEmpty()) {
throw new IllegalArgumentException("Argument " + name + " may not be empty.");
throw new IllegalArgumentException(format("Argument %s may not be empty.", name));
}
return argument;
}
@@ -68,11 +69,15 @@ public static boolean notNullAndSameClass(final Object object, final Object othe

public static BigInteger checkNotNegative(final BigInteger argument, final String name) {
if (checkNotNull(argument, name).compareTo(ZERO) < 0) {
throw new IllegalArgumentException("Argument " + name + " may not be negative.");
throw new IllegalArgumentException(format("Argument %s may not be negative.", name));
}
return argument;
}

public static String format(final String format, final Object... args) {
return String.format(Locale.ENGLISH, format, args);
}

public static String bytesToHexString(final byte[] bytes) {
checkNotNull(bytes, "bytes");
char[] hexChars = new char[bytes.length * 2];
Original file line number Diff line number Diff line change
@@ -18,6 +18,7 @@

import static io.parsingdata.metal.Util.checkNotNegative;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;

import java.io.IOException;
import java.io.UncheckedIOException;
@@ -37,7 +38,7 @@ public class ByteStreamSource extends Source {
@Override
protected byte[] getData(final BigInteger offset, final BigInteger length) {
if (!isAvailable(offset, length)) {
throw new IllegalStateException("Data to read is not available ([offset=" + offset + ";length=" + length + ";source=" + this + ").");
throw new IllegalStateException(format("Data to read is not available ([offset=%d;length=%d;source=%s).", offset, length, this));
}
try {
return input.read(offset, length.intValueExact());
Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
import static io.parsingdata.metal.Trampoline.intermediate;
import static io.parsingdata.metal.Util.checkNotNegative;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;

import java.math.BigInteger;
import java.util.Objects;
@@ -73,7 +74,7 @@ private static Trampoline<BigInteger> calculateTotalSize(final ImmutableList<Val
@Override
protected byte[] getData(final BigInteger offset, final BigInteger length) {
if (!isAvailable(offset, length)) {
throw new IllegalStateException("Data to read is not available (offset=" + offset + ";length=" + length + ";source=" + this + ").");
throw new IllegalStateException(format("Data to read is not available (offset=%d;length=%d;source=%s).", offset, length, this));
}
return getData(values, ZERO, ZERO, offset, length, new byte[length.intValueExact()]).computeResult();
}
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import static io.parsingdata.metal.Util.bytesToHexString;
import static io.parsingdata.metal.Util.checkNotNegative;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;

import java.math.BigInteger;
import java.util.Arrays;
@@ -37,7 +38,7 @@ public ConstantSource(final byte[] data) {
@Override
protected byte[] getData(final BigInteger offset, final BigInteger length) {
if (!isAvailable(offset, length)) {
throw new IllegalStateException("Data to read is not available ([offset=" + offset + ";length=" + length + ";source=" + this + ").");
throw new IllegalStateException(format("Data to read is not available ([offset=%d;length=%d;source=%s).", offset, length, this));
}
final byte[] outputData = new byte[length.intValueExact()];
System.arraycopy(data, offset.intValueExact(), outputData, 0, outputData.length);
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@
import static io.parsingdata.metal.Trampoline.intermediate;
import static io.parsingdata.metal.Util.checkNotNegative;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;

import java.math.BigInteger;
import java.util.Objects;
@@ -52,7 +53,7 @@ protected byte[] getData(final BigInteger offset, final BigInteger length) {
checkNotNegative(offset, "offset");
final byte[] data = getValue();
if (checkNotNegative(length, "length").add(offset).compareTo(BigInteger.valueOf(data.length)) > 0) {
throw new IllegalStateException("Data to read is not available ([offset=" + offset + ";length=" + length + ";source=" + this + ").");
throw new IllegalStateException(format("Data to read is not available ([offset=%d;length=%d;source=%s).", offset, length, this));
}
final byte[] outputData = new byte[length.intValueExact()];
System.arraycopy(data, offset.intValueExact(), outputData, 0, outputData.length);
@@ -68,12 +69,12 @@ private synchronized byte[] getValue() {
if (cache == null) {
final ImmutableList<Optional<Value>> results = dataExpression.eval(parseState, encoding);
if (results.size <= index) {
throw new IllegalStateException("ValueExpression dataExpression yields " + results.size + " result(s) (expected at least " + (index + 1) + ").");
throw new IllegalStateException(format("ValueExpression dataExpression yields %d result(s) (expected at least %d).", results.size, index+1));
}
cache = getValueAtIndex(results, index, 0)
.computeResult()
.map(Value::getValue)
.orElseThrow(() -> new IllegalStateException("ValueExpression dataExpression yields empty Value at index " + index + "."));
.orElseThrow(() -> new IllegalStateException(format("ValueExpression dataExpression yields empty Value at index %d.", index)));
}
return cache;
}
9 changes: 4 additions & 5 deletions core/src/main/java/io/parsingdata/metal/data/ParseState.java
Original file line number Diff line number Diff line change
@@ -21,15 +21,14 @@

import static io.parsingdata.metal.Util.checkNotNegative;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;
import static io.parsingdata.metal.data.Slice.createFromSource;

import java.math.BigInteger;
import java.util.Objects;
import java.util.Optional;

import io.parsingdata.metal.Util;
import io.parsingdata.metal.encoding.Encoding;
import io.parsingdata.metal.expression.value.ValueExpression;
import io.parsingdata.metal.token.Token;

public class ParseState {
@@ -60,7 +59,7 @@ public ParseState addBranch(final Token token) {

public ParseState closeBranch(final Token token) {
if (token.isIterable() && !iterations.head.left.equals(token)) {
throw new IllegalStateException(String.format("Cannot close branch for iterable token %s. Current iteration state is for token %s.", token.name, iterations.head.left.name));
throw new IllegalStateException(format("Cannot close branch for iterable token %s. Current iteration state is for token %s.", token.name, iterations.head.left.name));
}
return new ParseState(order.closeBranch(), source, offset, token.isIterable() ? iterations.tail : iterations);
}
@@ -81,8 +80,8 @@ public Optional<ParseState> seek(final BigInteger newOffset) {
return newOffset.compareTo(ZERO) >= 0 ? Optional.of(new ParseState(order, source, newOffset, iterations)) : Optional.empty();
}

public ParseState source(final ValueExpression dataExpression, final int index, final ParseState parseState, final Encoding encoding) {
return new ParseState(order, new DataExpressionSource(dataExpression, index, parseState, encoding), ZERO, iterations);
public ParseState withSource(final Source source) {
return new ParseState(order, source, ZERO, iterations);
}

public Optional<Slice> slice(final BigInteger length) {
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ public BinaryValueExpression(final ValueExpression left, final ValueExpression r
this.right = checkNotNull(right, "right");
}

public abstract Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding);
public abstract Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding);

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
Original file line number Diff line number Diff line change
@@ -37,9 +37,9 @@ public Cat(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
return ConcatenatedValueSource.create(ImmutableList.create(Optional.of(left)).add(Optional.of(right)))
.flatMap(source -> createFromSource(source, ZERO, left.getLength().add(right.getLength())))
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
return ConcatenatedValueSource.create(ImmutableList.create(Optional.of(leftValue)).add(Optional.of(rightValue)))
.flatMap(source -> createFromSource(source, ZERO, leftValue.getLength().add(rightValue.getLength())))
.map(source -> new Value(source, encoding));
}

Original file line number Diff line number Diff line change
@@ -33,58 +33,58 @@
* A {@link ValueExpression} that expands a result by copying and concatenating
* it a specified amount of times.
* <p>
* An Expand expression has two operands: <code>base</code> and
* An Expand expression has two operands: <code>bases</code> and
* <code>count</code> (both {@link ValueExpression}s). Both operands are
* evaluated. An <code>IllegalStateException</code> is thrown if evaluating
* <code>count</code> yields more than a single value. Multiple copies of the
* result of evaluating <code>base</code> are concatenated. The amount of copies
* result of evaluating <code>bases</code> are concatenated. The amount of copies
* equals the result of evaluating <code>count</code>.
*/
public class Expand implements ValueExpression {

public final ValueExpression base;
public final ValueExpression bases;
public final ValueExpression count;

public Expand(final ValueExpression base, final ValueExpression count) {
this.base = checkNotNull(base, "base");
public Expand(final ValueExpression bases, final ValueExpression count) {
this.bases = checkNotNull(bases, "bases");
this.count = checkNotNull(count, "count");
}

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
final ImmutableList<Optional<Value>> base = this.base.eval(parseState, encoding);
if (base.isEmpty()) {
return base;
final ImmutableList<Optional<Value>> baseList = bases.eval(parseState, encoding);
if (baseList.isEmpty()) {
return baseList;
}
final ImmutableList<Optional<Value>> count = this.count.eval(parseState, encoding);
if (count.size != 1 || !count.head.isPresent()) {
final ImmutableList<Optional<Value>> countList = count.eval(parseState, encoding);
if (countList.size != 1 || !countList.head.isPresent()) {
throw new IllegalArgumentException("Count must evaluate to a single non-empty value.");
}
return expand(base, count.head.get().asNumeric().intValueExact(), new ImmutableList<>()).computeResult();
return expand(baseList, countList.head.get().asNumeric().intValueExact(), new ImmutableList<>()).computeResult();
}

private Trampoline<ImmutableList<Optional<Value>>> expand(final ImmutableList<Optional<Value>> base, final int count, final ImmutableList<Optional<Value>> aggregate) {
if (count < 1) {
private Trampoline<ImmutableList<Optional<Value>>> expand(final ImmutableList<Optional<Value>> baseList, final int countValue, final ImmutableList<Optional<Value>> aggregate) {
if (countValue < 1) {
return complete(() -> aggregate);
}
return intermediate(() -> expand(base, count - 1, aggregate.add(base)));
return intermediate(() -> expand(baseList, countValue - 1, aggregate.add(baseList)));
}

@Override
public String toString() {
return getClass().getSimpleName() + "(" + base + "," + count + ")";
return getClass().getSimpleName() + "(" + bases + "," + count + ")";
}

@Override
public boolean equals(final Object obj) {
return Util.notNullAndSameClass(this, obj)
&& Objects.equals(base, ((Expand)obj).base)
&& Objects.equals(bases, ((Expand)obj).bases)
&& Objects.equals(count, ((Expand)obj).count);
}

@Override
public int hashCode() {
return Objects.hash(getClass(), base, count);
return Objects.hash(getClass(), bases, count);
}

}
Original file line number Diff line number Diff line change
@@ -58,18 +58,18 @@ public Fold(final ValueExpression values, final BinaryOperator<ValueExpression>

@Override
public ImmutableList<Optional<Value>> eval(final ParseState parseState, final Encoding encoding) {
final ImmutableList<Optional<Value>> initial = this.initial != null ? this.initial.eval(parseState, encoding) : new ImmutableList<>();
if (initial.size > 1) {
final ImmutableList<Optional<Value>> initialList = initial != null ? initial.eval(parseState, encoding) : new ImmutableList<>();
if (initialList.size > 1) {
return new ImmutableList<>();
}
final ImmutableList<Optional<Value>> values = prepareValues(this.values.eval(parseState, encoding));
if (values.isEmpty() || containsEmpty(values).computeResult()) {
return initial;
final ImmutableList<Optional<Value>> valueList = prepareValues(this.values.eval(parseState, encoding));
if (valueList.isEmpty() || containsEmpty(valueList).computeResult()) {
return initialList;
}
if (!initial.isEmpty()) {
return ImmutableList.create(fold(parseState, encoding, reducer, initial.head, values).computeResult());
if (!initialList.isEmpty()) {
return ImmutableList.create(fold(parseState, encoding, reducer, initialList.head, valueList).computeResult());
}
return ImmutableList.create(fold(parseState, encoding, reducer, values.head, values.tail).computeResult());
return ImmutableList.create(fold(parseState, encoding, reducer, valueList.head, valueList.tail).computeResult());
}

private Trampoline<Optional<Value>> fold(final ParseState parseState, final Encoding encoding, final BinaryOperator<ValueExpression> reducer, final Optional<Value> head, final ImmutableList<Optional<Value>> tail) {
@@ -92,7 +92,7 @@ private Trampoline<Boolean> containsEmpty(final ImmutableList<Optional<Value>> l
.orElseGet(() -> complete(() -> true));
}

protected abstract ImmutableList<Optional<Value>> prepareValues(ImmutableList<Optional<Value>> values);
protected abstract ImmutableList<Optional<Value>> prepareValues(ImmutableList<Optional<Value>> valueList);

protected abstract ValueExpression reduce(BinaryOperator<ValueExpression> reducer, Value head, Value tail);

Original file line number Diff line number Diff line change
@@ -39,8 +39,8 @@ public FoldLeft(final ValueExpression values, final BinaryOperator<ValueExpressi
}

@Override
protected ImmutableList<Optional<Value>> prepareValues(final ImmutableList<Optional<Value>> values) {
return reverse(values);
protected ImmutableList<Optional<Value>> prepareValues(final ImmutableList<Optional<Value>> valueList) {
return reverse(valueList);
}

@Override
Original file line number Diff line number Diff line change
@@ -38,8 +38,8 @@ public FoldRight(final ValueExpression values, final BinaryOperator<ValueExpress
}

@Override
protected ImmutableList<Optional<Value>> prepareValues(final ImmutableList<Optional<Value>> values) {
return values;
protected ImmutableList<Optional<Value>> prepareValues(final ImmutableList<Optional<Value>> valueList) {
return valueList;
}

@Override
Original file line number Diff line number Diff line change
@@ -35,8 +35,8 @@ public Add(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(left.asNumeric().add(right.asNumeric()), encoding));
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(leftValue.asNumeric().add(rightValue.asNumeric()), encoding));
}

}
Original file line number Diff line number Diff line change
@@ -40,11 +40,11 @@ public Div(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
if (right.asNumeric().equals(ZERO)) {
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
if (rightValue.asNumeric().equals(ZERO)) {
return Optional.empty();
}
return Optional.of(ConstantFactory.createFromNumeric(left.asNumeric().divide(right.asNumeric()), encoding));
return Optional.of(ConstantFactory.createFromNumeric(leftValue.asNumeric().divide(rightValue.asNumeric()), encoding));
}

}
Original file line number Diff line number Diff line change
@@ -40,11 +40,11 @@ public Mod(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
if (right.asNumeric().compareTo(ZERO) <= 0) {
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
if (rightValue.asNumeric().compareTo(ZERO) <= 0) {
return Optional.empty();
}
return Optional.of(ConstantFactory.createFromNumeric(left.asNumeric().mod(right.asNumeric()), encoding));
return Optional.of(ConstantFactory.createFromNumeric(leftValue.asNumeric().mod(rightValue.asNumeric()), encoding));
}

}
Original file line number Diff line number Diff line change
@@ -35,8 +35,8 @@ public Mul(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(left.asNumeric().multiply(right.asNumeric()), encoding));
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(leftValue.asNumeric().multiply(rightValue.asNumeric()), encoding));
}

}
Original file line number Diff line number Diff line change
@@ -35,8 +35,8 @@ public Sub(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(left.asNumeric().subtract(right.asNumeric()), encoding));
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
return Optional.of(ConstantFactory.createFromNumeric(leftValue.asNumeric().subtract(rightValue.asNumeric()), encoding));
}

}
Original file line number Diff line number Diff line change
@@ -36,10 +36,10 @@ public And(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = left.asBitSet();
leftBits.and(right.asBitSet());
return Optional.of(ConstantFactory.createFromBitSet(leftBits, left.getValue().length, encoding));
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = leftValue.asBitSet();
leftBits.and(rightValue.asBitSet());
return Optional.of(ConstantFactory.createFromBitSet(leftBits, leftValue.getValue().length, encoding));
}

}
Original file line number Diff line number Diff line change
@@ -36,10 +36,10 @@ public Or(final ValueExpression left, final ValueExpression right) {
}

@Override
public Optional<Value> eval(final Value left, final Value right, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = left.asBitSet();
leftBits.or(right.asBitSet());
final int minSize = Math.max(left.getValue().length, right.getValue().length);
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = leftValue.asBitSet();
leftBits.or(rightValue.asBitSet());
final int minSize = Math.max(leftValue.getValue().length, rightValue.getValue().length);
return Optional.of(ConstantFactory.createFromBitSet(leftBits, minSize, encoding));
}

Original file line number Diff line number Diff line change
@@ -37,9 +37,9 @@ public ShiftLeft(final ValueExpression operand, final ValueExpression positions)
}

@Override
public Optional<Value> eval(final Value operand, final Value positions, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = operand.asBitSet();
final int shiftLeft = positions.asNumeric().intValueExact();
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = leftValue.asBitSet();
final int shiftLeft = rightValue.asNumeric().intValueExact();
final int bitCount = leftBits.length() + shiftLeft;
final BitSet out = new BitSet(bitCount);
for (int i = leftBits.nextSetBit(0); i >= 0; i = leftBits.nextSetBit(i+1)) {
Original file line number Diff line number Diff line change
@@ -37,10 +37,10 @@ public ShiftRight(final ValueExpression operand, final ValueExpression positions
}

@Override
public Optional<Value> eval(final Value operand, final Value positions, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = operand.asBitSet();
final int shift = positions.asNumeric().intValueExact();
return Optional.of(ConstantFactory.createFromBitSet(leftBits.get(shift, Math.max(shift, leftBits.length())), operand.getValue().length, encoding));
public Optional<Value> eval(final Value leftValue, final Value rightValue, final ParseState parseState, final Encoding encoding) {
final BitSet leftBits = leftValue.asBitSet();
final int shift = rightValue.asNumeric().intValueExact();
return Optional.of(ConstantFactory.createFromBitSet(leftBits.get(shift, Math.max(shift, leftBits.length())), leftValue.getValue().length, encoding));
}

}
Original file line number Diff line number Diff line change
@@ -61,26 +61,26 @@ public ImmutableList<Optional<Value>> eval(final ParseState parseState, final En
}

private Optional<Value> getIteration(final ParseState parseState, final Encoding encoding) {
final BigInteger level = getLevel(parseState, encoding);
if (parseState.iterations.size <= level.longValue()) {
final BigInteger levelValue = getLevel(parseState, encoding);
if (parseState.iterations.size <= levelValue.longValue()) {
return Optional.empty();
}
return getIterationRecursive(parseState.iterations, level).computeResult();
return getIterationRecursive(parseState.iterations, levelValue).computeResult();
}

private BigInteger getLevel(final ParseState parseState, final Encoding encoding) {
final ImmutableList<Optional<Value>> evaluatedLevel = level.eval(parseState, encoding);
if (evaluatedLevel.size != 1 || !evaluatedLevel.head.isPresent()) {
final ImmutableList<Optional<Value>> levelList = level.eval(parseState, encoding);
if (levelList.size != 1 || !levelList.head.isPresent()) {
throw new IllegalArgumentException("Level must evaluate to a single non-empty value.");
}
return evaluatedLevel.head.get().asNumeric();
return levelList.head.get().asNumeric();
}

private Trampoline<Optional<Value>> getIterationRecursive(final ImmutableList<ImmutablePair<Token, BigInteger>> iterations, final BigInteger level) {
if (level.compareTo(ZERO) == 0) {
private Trampoline<Optional<Value>> getIterationRecursive(final ImmutableList<ImmutablePair<Token, BigInteger>> iterations, final BigInteger levelValue) {
if (levelValue.compareTo(ZERO) == 0) {
return complete(() -> Optional.of(createFromNumeric(iterations.head.right, DEFAULT_ENCODING)));
}
return intermediate(() -> getIterationRecursive(iterations.tail, level.subtract(ONE)));
return intermediate(() -> getIterationRecursive(iterations.tail, levelValue.subtract(ONE)));
}

@Override
Original file line number Diff line number Diff line change
@@ -59,12 +59,12 @@ private Ref(final T reference, final Predicate<ParseValue> predicate, final Valu

public static class NameRef extends Ref<String> {
public NameRef(final String reference) { this(reference, null); }
public NameRef(final String reference, final ValueExpression limit) { super(reference, (value) -> value.matches(reference), limit); }
public NameRef(final String reference, final ValueExpression limit) { super(reference, value -> value.matches(reference), limit); }
}

public static class DefinitionRef extends Ref<Token> {
public DefinitionRef(final Token reference) { this(reference, null); }
public DefinitionRef(final Token reference, final ValueExpression limit) { super(reference, (value) -> value.definition.equals(reference), limit); }
public DefinitionRef(final Token reference, final ValueExpression limit) { super(reference, value -> value.definition.equals(reference), limit); }
}

@Override
42 changes: 21 additions & 21 deletions core/src/main/java/io/parsingdata/metal/token/Sub.java
Original file line number Diff line number Diff line change
@@ -42,53 +42,53 @@
* in the input.
* <p>
* A Sub consists of a <code>token</code> (a {@link Token}) and an
* <code>address</code> (a {@link ValueExpression}). First
* <code>address</code> is evaluated. Then each resulting value is used as a
* <code>offsets</code> (a {@link ValueExpression}). First
* <code>offsets</code> is evaluated. Then each resulting value is used as a
* location in the input to parse <code>token</code> at. Sub succeeds if all
* parses of <code>token</code> at all locations succeed. Sub fails if
* <code>address</code> evaluates to a list of locations that is either empty
* <code>offsets</code> evaluates to a list of locations that is either empty
* or contains an invalid value.
*
* @see ValueExpression
*/
public class Sub extends Token {

public final Token token;
public final ValueExpression address;
public final ValueExpression offsets;

public Sub(final String name, final Token token, final ValueExpression address, final Encoding encoding) {
public Sub(final String name, final Token token, final ValueExpression offsets, final Encoding encoding) {
super(name, encoding);
this.token = checkNotNull(token, "token");
this.address = checkNotNull(address, "address");
this.offsets = checkNotNull(offsets, "offsets");
}

@Override
protected Optional<ParseState> parseImpl(final Environment environment) {
final ImmutableList<Optional<Value>> addresses = address.eval(environment.parseState, environment.encoding);
if (addresses.isEmpty()) {
final ImmutableList<Optional<Value>> offsetList = offsets.eval(environment.parseState, environment.encoding);
if (offsetList.isEmpty()) {
return failure();
}
return iterate(environment.addBranch(this), addresses)
return iterate(environment.addBranch(this), offsetList)
.computeResult()
.flatMap(nextParseState -> nextParseState.seek(environment.parseState.offset));
}

private Trampoline<Optional<ParseState>> iterate(final Environment environment, final ImmutableList<Optional<Value>> addresses) {
if (addresses.isEmpty()) {
private Trampoline<Optional<ParseState>> iterate(final Environment environment, final ImmutableList<Optional<Value>> offsetList) {
if (offsetList.isEmpty()) {
return complete(() -> success(environment.parseState.closeBranch(this)));
}
return addresses.head
.flatMap(address -> parse(environment, address.asNumeric()))
.map(nextParseState -> intermediate(() -> iterate(environment.withParseState(nextParseState), addresses.tail)))
return offsetList.head
.flatMap(offsetValue -> parse(environment, offsetValue.asNumeric()))
.map(nextParseState -> intermediate(() -> iterate(environment.withParseState(nextParseState), offsetList.tail)))
.orElseGet(() -> complete(Util::failure));
}

private Optional<ParseState> parse(final Environment environment, final BigInteger offset) {
if (hasRootAtOffset(environment.parseState.order, token.getCanonical(environment.parseState), offset, environment.parseState.source)) {
return success(environment.parseState.add(new ParseReference(offset, environment.parseState.source, token.getCanonical(environment.parseState))));
private Optional<ParseState> parse(final Environment environment, final BigInteger offsetValue) {
if (hasRootAtOffset(environment.parseState.order, token.getCanonical(environment.parseState), offsetValue, environment.parseState.source)) {
return success(environment.parseState.add(new ParseReference(offsetValue, environment.parseState.source, token.getCanonical(environment.parseState))));
}
return environment.parseState
.seek(offset)
.seek(offsetValue)
.map(newParseState -> token.parse(environment.withParseState(newParseState)))
.orElseGet(Util::failure);
}
@@ -100,19 +100,19 @@ public boolean isLocal() {

@Override
public String toString() {
return getClass().getSimpleName() + "(" + makeNameFragment() + token + "," + address + ")";
return getClass().getSimpleName() + "(" + makeNameFragment() + token + "," + offsets + ")";
}

@Override
public boolean equals(final Object obj) {
return super.equals(obj)
&& Objects.equals(token, ((Sub)obj).token)
&& Objects.equals(address, ((Sub)obj).address);
&& Objects.equals(offsets, ((Sub)obj).offsets);
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), token, address);
return Objects.hash(super.hashCode(), token, offsets);
}

}
3 changes: 2 additions & 1 deletion core/src/main/java/io/parsingdata/metal/token/Tie.java
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@

import io.parsingdata.metal.Trampoline;
import io.parsingdata.metal.Util;
import io.parsingdata.metal.data.DataExpressionSource;
import io.parsingdata.metal.data.Environment;
import io.parsingdata.metal.data.ImmutableList;
import io.parsingdata.metal.data.ParseState;
@@ -74,7 +75,7 @@ private Trampoline<Optional<ParseState>> iterate(final Environment environment,
}
return values.head
.map(value -> token
.parse(environment.withParseState(environment.parseState.source(dataExpression, index, environment.parseState, environment.encoding)))
.parse(environment.withParseState(environment.parseState.withSource(new DataExpressionSource(dataExpression, index, environment.parseState, environment.encoding))))
.map(nextParseState -> intermediate(() -> iterate(environment.withParseState(nextParseState), values.tail, index + 1, returnParseState)))
.orElseGet(() -> complete(Util::failure)))
.orElseGet(() -> complete(Util::failure));

This file was deleted.

12 changes: 6 additions & 6 deletions core/src/test/java/io/parsingdata/metal/token/UntilTest.java
Original file line number Diff line number Diff line change
@@ -59,9 +59,9 @@ public void threeNewLines() {
assertTrue(parseState.isPresent());
ImmutableList<ParseValue> values = getAllValues(parseState.get().order, "line");
assertEquals(3, values.size);
assertEquals(values.head.asString(), INPUT_3);
assertEquals(values.tail.head.asString(), INPUT_2);
assertEquals(values.tail.tail.head.asString(), INPUT_1);
assertEquals(INPUT_3, values.head.asString());
assertEquals(INPUT_2, values.tail.head.asString());
assertEquals(INPUT_1, values.tail.tail.head.asString());
}

@Test
@@ -70,9 +70,9 @@ public void untilInclusive() {
assertTrue(parseState.isPresent());
ImmutableList<ParseValue> values = getAllValues(parseState.get().order, "line");
assertEquals(3, values.size);
assertEquals(values.head.asString(), INPUT_3 + '\n');
assertEquals(values.tail.head.asString(), INPUT_2 + '\n');
assertEquals(values.tail.tail.head.asString(), INPUT_1 + '\n');
assertEquals(INPUT_3 + '\n', values.head.asString());
assertEquals(INPUT_2 + '\n', values.tail.head.asString());
assertEquals(INPUT_1 + '\n', values.tail.tail.head.asString());
}

@Test
Original file line number Diff line number Diff line change
@@ -23,6 +23,7 @@
import static io.parsingdata.metal.Shorthand.cat;
import static io.parsingdata.metal.Shorthand.con;
import static io.parsingdata.metal.Util.checkNotNull;
import static io.parsingdata.metal.Util.format;
import static io.parsingdata.metal.encoding.Encoding.DEFAULT_ENCODING;

import java.util.Arrays;
@@ -48,7 +49,7 @@ private GUID() {}
public static ValueExpression guid(final String guid) {
final String[] parts = checkNotNull(guid, "guid").split("-", -1);
if (parts.length != 5) {
throw new IllegalArgumentException("Invalid GUID string: " + guid);
throw new IllegalArgumentException(format("Invalid GUID string: %s", guid));
}
return (parseState, encoding) ->
// Note that GUID bytes differ from UUID bytes, as the first 3 parts can be reversed
29 changes: 17 additions & 12 deletions formats/src/main/java/io/parsingdata/metal/format/JPEG.java
Original file line number Diff line number Diff line change
@@ -39,31 +39,36 @@
*/
public final class JPEG {

public static final String MARKER = "marker";
public static final String IDENTIFIER = "identifier";
public static final String LENGTH = "length";
public static final String PAYLOAD = "payload";

private JPEG() {}

private static final Token HEADER =
seq("start of image",
def("marker", con(1), eq(con(0xff))),
def("identifier", con(1), eq(con(0xd8))));
def(MARKER, con(1), eq(con(0xff))),
def(IDENTIFIER, con(1), eq(con(0xd8))));

private static final Token FOOTER =
seq("end of image",
def("marker", con(1), eq(con(0xff))),
def("identifier", con(1), eq(con(0xd9))));
def(MARKER, con(1), eq(con(0xff))),
def(IDENTIFIER, con(1), eq(con(0xd9))));

private static final Token SIZED_SEGMENT =
seq("sized segment",
def("marker", con(1), eq(con(0xff))),
def("identifier", con(1), or(ltNum(con(0xd8)), gtNum(con(0xda)))),
def("length", con(2)),
def("payload", sub(last(ref("length")), con(2))));
def(MARKER, con(1), eq(con(0xff))),
def(IDENTIFIER, con(1), or(ltNum(con(0xd8)), gtNum(con(0xda)))),
def(LENGTH, con(2)),
def(PAYLOAD, sub(last(ref(LENGTH)), con(2))));

private static final Token SCAN_SEGMENT =
seq("scan segment",
def("marker", con(1), eq(con(0xff))),
def("identifier", con(1), eq(con(0xda))),
def("length", con(2)),
def("payload", sub(last(ref("length")), con(2))),
def(MARKER, con(1), eq(con(0xff))),
def(IDENTIFIER, con(1), eq(con(0xda))),
def(LENGTH, con(2)),
def(PAYLOAD, sub(last(ref(LENGTH)), con(2))),
rep(cho(def("scandata", con(1), not(eq(con(0xff)))),
def("escape", con(2), or(eq(con(0xff00)), and(gtNum(con(0xffcf)), ltNum(con(0xffd8))))))));

20 changes: 13 additions & 7 deletions formats/src/main/java/io/parsingdata/metal/format/PNG.java
Original file line number Diff line number Diff line change
@@ -33,6 +33,12 @@

public final class PNG {

public static final String LENGTH = "length";
public static final String TYPE = "type";
public static final String CRC_32 = "crc32";
public static final String IEND = "IEND";
public static final String DATA = "data";

private PNG() {}

private static final Token HEADER =
@@ -43,16 +49,16 @@ private PNG() {}

private static final Token FOOTER =
seq("footer",
def("footerlength", con(4), eqNum(con(0))),
def("footertype", con(4), eq(con("IEND"))),
def("footercrc32", con(4), eq(con(0xae, 0x42, 0x60, 0x82))));
def(LENGTH, con(4), eqNum(con(0))),
def(TYPE, con(4), eq(con(IEND))),
def(CRC_32, con(4), eq(con(0xae, 0x42, 0x60, 0x82))));

private static final Token STRUCT =
seq("chunk",
def("length", con(4)),
def("chunktype", con(4), not(eq(con("IEND")))),
def("chunkdata", last(ref("length"))),
def("crc32", con(4), eq(crc32(cat(last(ref("chunktype")), last(ref("chunkdata")))))));
def(LENGTH, con(4)),
def(TYPE, con(4), not(eq(con(IEND)))),
def(DATA, last(ref(LENGTH))),
def(CRC_32, con(4), eq(crc32(cat(last(ref(TYPE)), last(ref(DATA)))))));

public static final Token FORMAT =
seq("PNG", DEFAULT_ENCODING,
77 changes: 46 additions & 31 deletions formats/src/main/java/io/parsingdata/metal/format/ZIP.java
Original file line number Diff line number Diff line change
@@ -46,38 +46,53 @@
*/
public final class ZIP {

public static final String CRC_32 = "crc32";
public static final String COMPRESSED_SIZE = "compressedsize";
public static final String FILE_NAME_SIZE = "filenamesize";
public static final String EXTRA_FIELD_SIZE = "extrafieldsize";
public static final String EXTRACT_VERSION = "extractversion";
public static final String BIT_FLAG = "bitflag";
public static final String COMPRESSION_METHOD = "compressionmethod";
public static final String LAST_MOD_TIME = "lastmodtime";
public static final String LAST_MOD_DATE = "lastmoddate";
public static final String UNCOMPRESSED_SIZE = "uncompressedsize";
public static final String FILE_NAME = "filename";
public static final String COMPRESSED_DATA = "compresseddata";
public static final String DIR_SIGNATURE = "dirsignature";
public static final String EXTRA_FIELD = "extrafield";

private ZIP() {}

private static Token localFileBody(final String name, final int cm, final Expression crc, final Expression cs, final Expression usp) {
return
seq(name,
def("filesignature", con(4), eq(con(0x50, 0x4b, 0x03, 0x04))),
def("extractversion", con(2)),
def("bitflag", con(2)),
def("compressionmethod", con(2), eqNum(con(cm))),
def("lastmodtime", con(2)),
def("lastmoddate", con(2)),
def("crc32", con(4), crc),
def("compressedsize", con(4), cs),
def("uncompressedsize", con(4), usp),
def("filenamesize", con(2)),
def("extrafieldsize", con(2)),
def("filename", last(ref("filenamesize"))),
def("extrafield", last(ref("extrafieldsize"))));
def(EXTRACT_VERSION, con(2)),
def(BIT_FLAG, con(2)),
def(COMPRESSION_METHOD, con(2), eqNum(con(cm))),
def(LAST_MOD_TIME, con(2)),
def(LAST_MOD_DATE, con(2)),
def(CRC_32, con(4), crc),
def(COMPRESSED_SIZE, con(4), cs),
def(UNCOMPRESSED_SIZE, con(4), usp),
def(FILE_NAME_SIZE, con(2)),
def(EXTRA_FIELD_SIZE, con(2)),
def(FILE_NAME, last(ref(FILE_NAME_SIZE))),
def(EXTRA_FIELD, last(ref(EXTRA_FIELD_SIZE))));
}

private static final Token LOCAL_DEFLATED_FILE =
seq("localdeflatedfile",
localFileBody("", 8, TRUE, TRUE, TRUE),
def("compresseddata", last(ref("compressedsize")), eqNum(crc32(inflate(SELF)), last(ref("crc32")))));
def(COMPRESSED_DATA, last(ref(COMPRESSED_SIZE)), eqNum(crc32(inflate(SELF)), last(ref(CRC_32)))));

private static final Token LOCAL_EMPTY_FILE =
localFileBody("localemptyfile", 0, eqNum(con(0)), eqNum(con(0)), eqNum(con(0)));

private static final Token LOCAL_STORED_FILE =
seq("localstoredfile",
localFileBody("", 0, TRUE, TRUE, eq(last(ref("compressedsize")))),
def("compresseddata", last(ref("compressedsize")), eqNum(crc32(SELF), last(ref("crc32")))));
localFileBody("", 0, TRUE, TRUE, eq(last(ref(COMPRESSED_SIZE)))),
def(COMPRESSED_DATA, last(ref(COMPRESSED_SIZE)), eqNum(crc32(SELF), last(ref(CRC_32)))));

private static final Token FILES =
rep("files",
@@ -87,25 +102,25 @@ private static Token localFileBody(final String name, final int cm, final Expres

private static final Token DIR_ENTRY =
seq("direntry",
def("dirsignature", con(4), eq(con(0x50, 0x4b, 0x01, 0x02))),
def(DIR_SIGNATURE, con(4), eq(con(0x50, 0x4b, 0x01, 0x02))),
def("makeversion", con(2)),
def("extractversion", con(2)),
def("bitflag", con(2)),
def("compressionmethod", con(2)),
def("lastmodtime", con(2)),
def("lastmoddate", con(2)),
def("crc32", con(4)),
def("compressedsize", con(4)),
def("uncompressedsize", con(4)),
def("filenamesize", con(2)),
def("extrafieldsize", con(2)),
def(EXTRACT_VERSION, con(2)),
def(BIT_FLAG, con(2)),
def(COMPRESSION_METHOD, con(2)),
def(LAST_MOD_TIME, con(2)),
def(LAST_MOD_DATE, con(2)),
def(CRC_32, con(4)),
def(COMPRESSED_SIZE, con(4)),
def(UNCOMPRESSED_SIZE, con(4)),
def(FILE_NAME_SIZE, con(2)),
def(EXTRA_FIELD_SIZE, con(2)),
def("filecommentsize", con(2)),
def("filedisk", con(2), eqNum(con(0))),
def("intfileattr", con(2)),
def("extfileattr", con(4)),
def("offset", con(4)),
def("filename", last(ref("filenamesize"))),
def("extrafield", last(ref("extrafieldsize"))),
def(FILE_NAME, last(ref(FILE_NAME_SIZE))),
def(EXTRA_FIELD, last(ref(EXTRA_FIELD_SIZE))),
def("filecomment", last(ref("filecommentsize"))));

private static final Token DIRS =
@@ -117,10 +132,10 @@ private static Token localFileBody(final String name, final int cm, final Expres
def("endofdirsignature", con(4), eq(con(0x50, 0x4b, 0x05, 0x06))),
def("disknumber", con(2), eqNum(con(0))),
def("dirdisk", con(2), eqNum(con(0))),
def("numlocaldirs", con(2), eqNum(count(ref("dirsignature")))),
def("numlocaldirs", con(2), eqNum(count(ref(DIR_SIGNATURE)))),
def("numtotaldirs", con(2), eqNum(last(ref("numlocaldirs")))),
def("dirsize", con(4), eqNum(sub(offset(last(ref("endofdirsignature"))), offset(first(ref("dirsignature")))))),
def("diroffset", con(4), eqNum(offset(first(ref("dirsignature"))))),
def("dirsize", con(4), eqNum(sub(offset(last(ref("endofdirsignature"))), offset(first(ref(DIR_SIGNATURE)))))),
def("diroffset", con(4), eqNum(offset(first(ref(DIR_SIGNATURE))))),
def("commentsize", con(2)),
def("comment", last(ref("commentsize"))));