Skip to content
This repository has been archived by the owner on Jan 27, 2025. It is now read-only.

Commit

Permalink
Implement "strict" mode. (#192)
Browse files Browse the repository at this point in the history
Levels of strictness:

- `PROCESS`: Abort process by throwing an exception.
- `RECORD`: Ignore (skip) record and log an error.
- `EXPRESSION`: Ignore (skip) expression and log a warning.

Introduces `FixProcessException` to differentiate from `FixExecutionException`: The latter indicating potentially data-dependent issues that should be subject to strictness handling, while the former should only refer to static issues with the usage of Fix expressions.
  • Loading branch information
blackwinter committed Mar 30, 2022
1 parent b491cbb commit 1c164e8
Show file tree
Hide file tree
Showing 17 changed files with 274 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@

import org.metafacture.framework.MetafactureException;

/**
* Indicates dynamic (i.e., data-dependent) issues during Fix execution that
* should be subject to {@link Metafix.Strictness strictness} handling.
*
* @see FixProcessException
*/
public class FixExecutionException extends MetafactureException {

public FixExecutionException(final String message) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2022 hbz NRW
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.metafacture.metafix;

import org.metafacture.framework.MetafactureException;

/**
* Indicates static (i.e., data-independent) issues with the usage of Fix
* expressions.
*
* @see FixExecutionException
*/
public class FixProcessException extends MetafactureException {

public FixProcessException(final String message) {
super(message);
}

public FixProcessException(final String message, final Throwable cause) {
super(message, cause);
}

}
58 changes: 58 additions & 0 deletions metafix/src/main/java/org/metafacture/metafix/Metafix.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;

/**
* Transforms a data stream sent via the {@link StreamReceiver} interface. Use
Expand All @@ -61,6 +62,8 @@ public class Metafix implements StreamPipe<StreamReceiver>, Maps { // checkstyle
public static final String VAR_END = "]";
public static final String VAR_START = "$[";

public static final Strictness DEFAULT_STRICTNESS = Strictness.PROCESS;

public static final Map<String, String> NO_VARS = Collections.emptyMap();

private static final Logger LOG = LoggerFactory.getLogger(Metafix.class);
Expand All @@ -79,6 +82,7 @@ public class Metafix implements StreamPipe<StreamReceiver>, Maps { // checkstyle
private List<Value> entities = new ArrayList<>();
private Record currentRecord = new Record();
private StreamReceiver outputStreamReceiver;
private Strictness strictness = DEFAULT_STRICTNESS;
private String fixFile;
private String recordIdentifier;
private int entityCount;
Expand Down Expand Up @@ -325,4 +329,58 @@ public String putValue(final String mapName, final String key, final String valu
return maps.computeIfAbsent(mapName, k -> new HashMap<>()).put(key, value);
}

public void setStrictness(final Strictness strictness) {
this.strictness = strictness != null ? strictness : DEFAULT_STRICTNESS;
}

public Strictness getStrictness() {
return strictness;
}

public enum Strictness {

/**
* Aborts process by throwing an exception.
*/
PROCESS {
@Override
protected void handleInternal(final FixExecutionException exception, final Record record) {
throw exception;
}
},

/**
* Ignores (skips) record and logs an error.
*/
RECORD {
@Override
protected void handleInternal(final FixExecutionException exception, final Record record) {
log(exception, LOG::error);
record.setReject(true); // TODO: Skip remaining expressions?
}
},

/**
* Ignores (skips) expression and logs a warning.
*/
EXPRESSION {
@Override
protected void handleInternal(final FixExecutionException exception, final Record record) {
log(exception, LOG::warn);
}
};

public void handle(final FixExecutionException exception, final Record record) {
LOG.debug("Current record: {}", record);
handleInternal(exception, record);
}

protected abstract void handleInternal(FixExecutionException exception, Record record);

protected void log(final FixExecutionException exception, final BiConsumer<String, Throwable> logger) {
logger.accept(exception.getMessage(), exception.getCause());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ else if (e instanceof MethodCall) {
processFunction((MethodCall) e, params);
}
else {
throw new FixExecutionException(executionExceptionMessage(e));
throw new FixProcessException(executionExceptionMessage(e));
}
});
}
Expand Down Expand Up @@ -173,15 +173,26 @@ private void processExpression(final Expression expression, final Consumer<Strin
}

private void processFix(final Supplier<String> messageSupplier, final Runnable runnable) {
final FixExecutionException exception;

try {
runnable.run();
return;
}
catch (final FixExecutionException e) {
catch (final FixProcessException e) {
throw e; // TODO: Add nesting information?
}
catch (final FixExecutionException e) {
exception = e; // TODO: Add nesting information?
}
catch (final IllegalStateException | NumberFormatException e) {
exception = new FixExecutionException(messageSupplier.get(), e);
}
catch (final RuntimeException e) { // checkstyle-disable-line IllegalCatch
throw new FixExecutionException(messageSupplier.get(), e);
throw new FixProcessException(messageSupplier.get(), e);
}

metafix.getStrictness().handle(exception, record);
}

private String executionExceptionMessage(final Expression expression) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ public void shouldLookupInSeparateExternalFileMap() {
public void shouldNotLookupInRelativeExternalFileMapFromInlineScript() {
final String mapFile = "../maps/test.csv";

MetafixTestHelpers.assertThrowsCause(IllegalArgumentException.class, "Cannot resolve relative path: " + mapFile, () ->
MetafixTestHelpers.assertProcessException(IllegalArgumentException.class, "Cannot resolve relative path: " + mapFile, () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
LOOKUP + " '" + mapFile + "')"
),
Expand Down Expand Up @@ -299,7 +299,7 @@ public void shouldNotLookupInUnknownInternalMap() {

@Test
public void shouldFailLookupInUnknownExternalMap() {
MetafixTestHelpers.assertThrowsCause(MorphExecutionException.class, "File not found: testMap.csv", () ->
MetafixTestHelpers.assertProcessException(MorphExecutionException.class, "File not found: testMap.csv", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
LOOKUP + " 'testMap.csv')"
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ public void shouldCapitalizeStringsInArray() {

@Test
public void shouldNotCapitalizeArray() {
MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertExecutionException(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"capitalize('title')"
),
Expand Down Expand Up @@ -431,7 +431,7 @@ public void parseTextMixedGroups() {

@Test
public void parseTextEscapedGroups() {
MetafixTestHelpers.assertThrowsCause(IllegalArgumentException.class, "No group with name <c>", () ->
MetafixTestHelpers.assertProcessException(IllegalArgumentException.class, "No group with name <c>", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"parse_text(data, '(?<a>.)(.)\\\\(?<c>.\\\\)')"
),
Expand All @@ -448,7 +448,7 @@ public void parseTextEscapedGroups() {

@Test
public void parseTextQuotedGroups() {
MetafixTestHelpers.assertThrowsCause(IllegalArgumentException.class, "No group with name <c>", () ->
MetafixTestHelpers.assertProcessException(IllegalArgumentException.class, "No group with name <c>", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"parse_text(data, '(?<a>.)(.)\\\\Q(?<c>.)\\\\E')"
),
Expand Down Expand Up @@ -669,7 +669,7 @@ public void shouldAppendValueInEntireArray() {
@Test
// See https://github.com/metafacture/metafacture-fix/issues/100
public void shouldNotAppendValueToArray() {
MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertExecutionException(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"append('animals[]', ' is cool')"
),
Expand All @@ -691,7 +691,7 @@ public void shouldNotAppendValueToArray() {
@Test
// See https://github.com/metafacture/metafacture-fix/issues/100
public void shouldNotAppendValueToHash() {
MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Hash", () ->
MetafixTestHelpers.assertExecutionException(IllegalStateException.class, "Expected String, got Hash", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"append('animals', ' is cool')"
),
Expand Down Expand Up @@ -1126,7 +1126,7 @@ public void shouldPrependValueInArraySubField() {
@Test
// See https://github.com/metafacture/metafacture-fix/issues/100
public void shouldNotPrependValueToArray() {
MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertExecutionException(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"prepend('animals[]', 'cool ')"
),
Expand Down Expand Up @@ -1592,7 +1592,7 @@ public void shouldSortFieldNumerically() {

@Test
public void shouldFailToSortNumericallyWithInvalidNumber() {
MetafixTestHelpers.assertThrowsCause(NumberFormatException.class, "For input string: \"x\"", () ->
MetafixTestHelpers.assertExecutionException(NumberFormatException.class, "For input string: \"x\"", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"sort_field(numbers, numeric: 'true')"
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,7 @@ public void addFieldToObjectByIndexMissing() {
}

private void assertThrowsOnEmptyRecord(final String index) {
MetafixTestHelpers.assertThrowsCause(IllegalArgumentException.class, "Using ref, but can't find: " + index + " in: {}", () -> {
MetafixTestHelpers.assertProcessException(IllegalArgumentException.class, "Using ref, but can't find: " + index + " in: {}", () -> {
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"add_field('animals[]." + index + ".kind','nice')"
),
Expand Down Expand Up @@ -2150,7 +2150,7 @@ public void accessArrayByIndex() {

@Test
public void shouldNotAccessArrayImplicitly() {
MetafixTestHelpers.assertThrowsCause(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertExecutionException(IllegalStateException.class, "Expected String, got Array", () ->
MetafixTestHelpers.assertFix(streamReceiver, Arrays.asList(
"upcase('name')"
),
Expand Down
Loading

0 comments on commit 1c164e8

Please sign in to comment.