Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 70 additions & 26 deletions metamorph/src/main/java/org/metafacture/metamorph/Metamorph.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
* Transforms a data stream sent via the {@link StreamReceiver} interface. Use
Expand Down Expand Up @@ -100,8 +101,9 @@ public final class Metamorph implements StreamPipe<StreamReceiver>, NamedValuePi
private MorphErrorHandler errorHandler = new DefaultErrorHandler();
private int recordCount;
private final List<FlushListener> recordEndListener = new ArrayList<>();

private final Deque<EntityEntry> elseNestedEntities = new LinkedList<>();
private boolean elseNested;
private boolean elseNestedEntityStarted;
private String currentLiteralName;

protected Metamorph() {
Expand Down Expand Up @@ -239,16 +241,16 @@ protected void registerNamedValueReceiver(final String source, final NamedValueR
@Override
public void startRecord(final String identifier) {
flattener.startRecord(identifier);
elseNestedEntities.clear();
entityCountStack.clear();

entityCount = 0;
currentEntityCount = 0;
entityCountStack.push(Integer.valueOf(entityCount));

++recordCount;
recordCount %= Integer.MAX_VALUE;

entityCountStack.add(Integer.valueOf(entityCount));

final String identifierFinal = identifier;

outputStreamReceiver.startRecord(identifierFinal);
Expand All @@ -262,12 +264,13 @@ public void endRecord() {
}

outputStreamReceiver.endRecord();
entityCountStack.removeLast();
if (!entityCountStack.isEmpty()) {
flattener.endRecord();

entityCountStack.pop();

if (!elseNestedEntities.isEmpty() || !entityCountStack.isEmpty()) {
throw new IllegalStateException(ENTITIES_NOT_BALANCED);
}

flattener.endRecord();
}

@Override
Expand All @@ -281,13 +284,16 @@ public void startEntity(final String name) {
entityCountStack.push(Integer.valueOf(entityCount));

flattener.startEntity(name);
elseNestedEntities.push(new EntityEntry(flattener));
}

@Override
public void endEntity() {
dispatch(flattener.getCurrentPath(), "", getElseSources(), true);
currentEntityCount = entityCountStack.pop().intValue();
flattener.endEntity();

elseNestedEntities.pop();
currentEntityCount = entityCountStack.pop().intValue();
}

@Override
Expand Down Expand Up @@ -322,30 +328,38 @@ private void dispatch(final String path, final String value, final List<NamedVal
send(path, value, matchingData);
}
else if (fallbackReceiver != null) {
if (endEntity) {
if (elseNestedEntityStarted) {
outputStreamReceiver.endEntity();
elseNestedEntityStarted = false;
}
}
else {
final String entityName = elseNested ? flattener.getCurrentEntityName() : null;
dispatchFallback(path, endEntity, k -> send(escapeFeedbackChar(k), value, fallbackReceiver));
}
}

if (entityName != null) {
if (getData(entityName) == null) {
if (!elseNestedEntityStarted) {
outputStreamReceiver.startEntity(entityName);
elseNestedEntityStarted = true;
}
private void dispatchFallback(final String path, final boolean endEntity, final Consumer<String> consumer) {
final EntityEntry entityEntry = elseNested ? elseNestedEntities.peek() : null;

send(escapeFeedbackChar(currentLiteralName), value, fallbackReceiver);
if (endEntity) {
if (entityEntry != null && entityEntry.getStarted()) {
outputStreamReceiver.endEntity();
}
}
else if (entityEntry != null) {
if (getData(entityEntry.getPath()) == null) {
final Deque<String> entities = new LinkedList<>();

for (final EntityEntry e : elseNestedEntities) {
if (e.getStarted()) {
break;
}

e.setStarted(true);
entities.push(e.getName());
}
else {
send(escapeFeedbackChar(path), value, fallbackReceiver);
}

entities.forEach(outputStreamReceiver::startEntity);
consumer.accept(currentLiteralName);
}
}
else {
consumer.accept(path);
}
}

private List<NamedValueReceiver> getData(final String path) {
Expand Down Expand Up @@ -468,4 +482,34 @@ public SourceLocation getSourceLocation() {
return null;
}

private static class EntityEntry {

private final String name;
private final String path;

private boolean started;

EntityEntry(final StreamFlattener flattener) {
name = flattener.getCurrentEntityName();
path = flattener.getCurrentPath();
}

private String getName() {
return name;
}

private String getPath() {
return path;
}

private void setStarted(final boolean started) {
this.started = started;
}

private boolean getStarted() {
return started;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,226 @@ public void issue338_shouldPreserveSameEntitiesInElseNestedSource() {
);
}

@Test
public void issue374_shouldPropagateArrayMarkersInElseNestedSource() {
assertMorph(receiver,
"<rules>" +
" <data source='_elseNested' />" +
"</rules>",
i -> {
i.startRecord("1");
i.startEntity("author[]");
i.startEntity("");
i.literal("@type", "Person");
i.literal("name", "Katja Königstein-Lüdersdorff");
i.endEntity();
i.startEntity("");
i.literal("@type", "Person");
i.literal("name", "Corinna Peters");
i.endEntity();
i.startEntity("");
i.literal("@type", "Person");
i.literal("name", "Oleg Tjulenev");
i.endEntity();
i.startEntity("");
i.literal("@type", "Person");
i.literal("name", "Claudia Vogeler");
i.endEntity();
i.endEntity();
i.endRecord();
},
(o, f) -> {
o.get().startRecord("1");
o.get().startEntity("author[]");
o.get().startEntity("");
o.get().literal("@type", "Person");
o.get().literal("name", "Katja Königstein-Lüdersdorff");
o.get().endEntity();
o.get().startEntity("");
o.get().literal("@type", "Person");
o.get().literal("name", "Corinna Peters");
o.get().endEntity();
o.get().startEntity("");
o.get().literal("@type", "Person");
o.get().literal("name", "Oleg Tjulenev");
o.get().endEntity();
o.get().startEntity("");
o.get().literal("@type", "Person");
o.get().literal("name", "Claudia Vogeler");
f.apply(2).endEntity();
o.get().endRecord();
}
);
}

@Test
public void issue378_shouldOutputMoreThanTwoLevelsInElseNestedSource() {
assertMorph(receiver,
"<rules>" +
" <data source='_elseNested' />" +
"</rules>",
i -> {
i.startRecord("1");
i.startEntity("mods");
i.literal("ID", "duepublico_mods_00074526");
i.startEntity("name");
i.literal("type", "personal");
i.literal("type", "simple");
i.startEntity("displayForm");
i.literal("value", "Armbruster, André");
i.endEntity();
i.startEntity("role");
i.startEntity("roleTerm");
i.literal("authority", "marcrelator");
i.literal("type", "code");
i.literal("value", "aut");
i.endEntity();
i.startEntity("roleTerm");
i.literal("authority", "marcrelator");
i.literal("type", "text");
i.literal("value", "Author");
i.endEntity();
i.endEntity();
i.startEntity("nameIdentifier");
i.literal("type", "gnd");
i.literal("value", "1081830107");
i.endEntity();
i.startEntity("namePart");
i.literal("type", "family");
i.literal("value", "Armbruster");
i.endEntity();
i.startEntity("namePart");
i.literal("type", "given");
i.literal("value", "André");
i.endEntity();
i.endEntity();
i.endEntity();
i.endRecord();
},
(o, f) -> {
o.get().startRecord("1");
o.get().startEntity("mods");
o.get().literal("ID", "duepublico_mods_00074526");
o.get().startEntity("name");
o.get().literal("type", "personal");
o.get().literal("type", "simple");
o.get().startEntity("displayForm");
o.get().literal("value", "Armbruster, André");
o.get().endEntity();
o.get().startEntity("role");
o.get().startEntity("roleTerm");
o.get().literal("authority", "marcrelator");
o.get().literal("type", "code");
o.get().literal("value", "aut");
o.get().endEntity();
o.get().startEntity("roleTerm");
o.get().literal("authority", "marcrelator");
o.get().literal("type", "text");
o.get().literal("value", "Author");
f.apply(2).endEntity();
o.get().startEntity("nameIdentifier");
o.get().literal("type", "gnd");
o.get().literal("value", "1081830107");
o.get().endEntity();
o.get().startEntity("namePart");
o.get().literal("type", "family");
o.get().literal("value", "Armbruster");
o.get().endEntity();
o.get().startEntity("namePart");
o.get().literal("type", "given");
o.get().literal("value", "André");
f.apply(3).endEntity();
o.get().endRecord();
}
);
}

@Test
public void shouldOutputMoreThanTwoLevelsInElseNestedSourceWithModifications() {
assertMorph(receiver,
"<rules>" +
" <entity name='name' flushWith='record'>" +
" <data source='mods.name.displayForm.value' name='displayForm' />" +
" <data source='mods.name.namePart.value' />" +
" </entity>" +
" <data source='_elseNested' />" +
"</rules>",
i -> {
i.startRecord("1");
i.startEntity("mods");
i.literal("ID", "duepublico_mods_00074526");
i.startEntity("name");
i.literal("type", "personal");
i.literal("type", "simple");
i.startEntity("displayForm");
i.literal("value", "Armbruster, André");
i.endEntity();
i.startEntity("role");
i.startEntity("roleTerm");
i.literal("authority", "marcrelator");
i.literal("type", "code");
i.literal("value", "aut");
i.endEntity();
i.startEntity("roleTerm");
i.literal("authority", "marcrelator");
i.literal("type", "text");
i.literal("value", "Author");
i.endEntity();
i.endEntity();
i.startEntity("nameIdentifier");
i.literal("type", "gnd");
i.literal("value", "1081830107");
i.endEntity();
i.startEntity("namePart");
i.literal("type", "family");
i.literal("value", "Armbruster");
i.endEntity();
i.startEntity("namePart");
i.literal("type", "given");
i.literal("value", "André");
i.endEntity();
i.endEntity();
i.endEntity();
i.endRecord();
},
(o, f) -> {
o.get().startRecord("1");
o.get().startEntity("mods");
o.get().literal("ID", "duepublico_mods_00074526");
o.get().startEntity("name");
o.get().literal("type", "personal");
o.get().literal("type", "simple");
o.get().startEntity("role");
o.get().startEntity("roleTerm");
o.get().literal("authority", "marcrelator");
o.get().literal("type", "code");
o.get().literal("value", "aut");
o.get().endEntity();
o.get().startEntity("roleTerm");
o.get().literal("authority", "marcrelator");
o.get().literal("type", "text");
o.get().literal("value", "Author");
f.apply(2).endEntity();
o.get().startEntity("nameIdentifier");
o.get().literal("type", "gnd");
o.get().literal("value", "1081830107");
o.get().endEntity();
o.get().startEntity("namePart");
o.get().literal("type", "family");
o.get().endEntity();
o.get().startEntity("namePart");
o.get().literal("type", "given");
f.apply(3).endEntity();
o.get().startEntity("name");
o.get().literal("displayForm", "Armbruster, André");
o.get().literal("mods.name.namePart.value", "Armbruster");
o.get().literal("mods.name.namePart.value", "André");
o.get().endEntity();
o.get().endRecord();
}
);
}

@Test
public void shouldHandleUnmatchedLiteralsAndEntitiesInElseNestedSource() {
assertMorph(receiver,
Expand Down