diff --git a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java index 2271dafaa..912147a7d 100644 --- a/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java +++ b/metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java @@ -49,9 +49,6 @@ public final class MarcXmlEncoder extends DefaultStreamPipe"; - private static final String ROOT_CLOSE = ""; - private enum Tag { collection(" xmlns%s=\"" + NAMESPACE + "\"%s"), @@ -106,7 +103,6 @@ public String close(final Object[] args) { private static final int TAG_END = 3; private final Encoder encoder = new Encoder(); - private final Marc21Decoder decoder = new Marc21Decoder(); private final Marc21Encoder wrapper = new Marc21Encoder(); private DefaultStreamPipe> pipe; @@ -115,6 +111,7 @@ public String close(final Object[] args) { * Creates an instance of {@link MarcXmlEncoder}. */ public MarcXmlEncoder() { + final Marc21Decoder decoder = new Marc21Decoder(); decoder.setEmitLeaderAsWhole(true); wrapper @@ -136,7 +133,6 @@ public void setEmitNamespace(final boolean emitNamespace) { /** * Sets the flag to decide whether to omit the XML declaration. - * * Default value: {@value #OMIT_XML_DECLARATION} * * @param currentOmitXmlDeclaration true if the XML declaration is omitted, otherwise @@ -148,7 +144,6 @@ public void omitXmlDeclaration(final boolean currentOmitXmlDeclaration) { /** * Sets the XML version. - * * Default value: {@value #XML_VERSION} * * @param xmlVersion the XML version @@ -159,7 +154,6 @@ public void setXmlVersion(final String xmlVersion) { /** * Sets the XML encoding. - * * Default value: {@value #XML_ENCODING} * * @param xmlEncoding the XML encoding @@ -173,7 +167,6 @@ public void setXmlEncoding(final String xmlEncoding) { * If true, the input data is validated to ensure correct MARC21. Also the leader may be generated. * It acts as a wrapper: the input is piped to {@link org.metafacture.biblio.marc21.Marc21Encoder}, whose output is piped to {@link org.metafacture.biblio.marc21.Marc21Decoder}, whose output is piped to {@link org.metafacture.biblio.marc21.MarcXmlEncoder}. * This validation and treatment of the leader is more safe but comes with a performance impact. - * * Default value: {@value #ENSURE_CORRECT_MARC21_XML} * * @param ensureCorrectMarc21Xml if true the input data is validated to ensure correct MARC21. Also the leader may be generated. @@ -184,7 +177,6 @@ public void setEnsureCorrectMarc21Xml(final boolean ensureCorrectMarc21Xml) { /** * Formats the resulting xml by indentation. Aka "pretty printing". - * * Default value: {@value #PRETTY_PRINTED} * * @param formatted true if formatting is activated, otherwise false @@ -247,11 +239,12 @@ private static class Encoder extends DefaultStreamPipe> { private String currentEntity = ""; private boolean emitNamespace = true; - private Object[] namespacePrefix = new Object[]{emitNamespace ? NAMESPACE_PREFIX : EMPTY}; + private Object[] namespacePrefix = new Object[]{NAMESPACE_PREFIX}; private int indentationLevel; private boolean formatted = PRETTY_PRINTED; private int recordAttributeOffset; + private int recordLeaderOffset; private Encoder() { } @@ -294,7 +287,7 @@ public void startRecord(final String identifier) { writeTag(Tag.record::open); recordAttributeOffset = builder.length() - 1; prettyPrintNewLine(); - + recordLeaderOffset = builder.length(); incrementIndentationLevel(); } @@ -353,7 +346,7 @@ else if (!appendLeader(name, value)) { if (value != null) { writeEscaped(value.trim()); } - writeTag(Tag.controlfield::close); + writeTag(Tag.controlfield::close, false); prettyPrintNewLine(); } } @@ -408,9 +401,20 @@ private void writeFooter() { * @param str the unescaped sequence to be written */ private void writeRaw(final String str) { + builder.append(str); } + /** + * Writes the unescaped sequence to the leader position. + * + * @param str the unescaped sequence to be written to the leader position + */ + private void writeRawLeader(final String str) { + builder.insert(recordLeaderOffset, str); + recordLeaderOffset = recordLeaderOffset + str.length(); + } + private boolean appendLeader(final String name, final String value) { if (name.equals(Marc21EventNames.LEADER_ENTITY)) { leaderBuilder.append(value); @@ -432,11 +436,11 @@ private void writeEscaped(final String str) { private void writeLeader() { final String leader = leaderBuilder.toString(); - if (!leader.isEmpty()) { + if (leaderBuilder.length() > 0) { prettyPrintIndentation(); - writeTag(Tag.leader::open); - writeRaw(leader); - writeTag(Tag.leader::close); + writeTagLeader(Tag.leader::open); + writeRawLeader(leader); + writeTagLeader(Tag.leader::close); prettyPrintNewLine(); } } @@ -447,6 +451,10 @@ private void writeTag(final Function function, final Object... writeRaw(function.apply(allArgs)); } + private void writeTagLeader(final Function function) { + writeRawLeader(function.apply(namespacePrefix)); + } + private void prettyPrintIndentation() { if (formatted) { final String prefix = String.join("", Collections.nCopies(indentationLevel, INDENT)); diff --git a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java index 04e40652e..23835ad84 100644 --- a/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java +++ b/metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java @@ -25,6 +25,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; /** @@ -249,12 +250,26 @@ private void issue336_createRecordWithTopLevelLeader(final MarcXmlEncoder encode encoder.endRecord(); encoder.closeStream(); String expected = XML_DECLARATION + XML_ROOT_OPEN - + "8u3287432" + - "" + expectedLeader + "" + XML_MARC_COLLECTION_END_TAG; + + "" + expectedLeader + "" + + "8u3287432" + XML_MARC_COLLECTION_END_TAG; String actual = resultCollector.toString(); assertEquals(expected, actual); } + @Test + public void issue548_failWhenLeaderIsNotFirst() { + encoder.startRecord("1"); + encoder.literal("001", "8u3287432"); + encoder.literal(Marc21EventNames.LEADER_ENTITY, "00000naa a2200000uc 4500"); + encoder.endRecord(); + encoder.closeStream(); + String expected = XML_DECLARATION + XML_ROOT_OPEN + + "8u3287432" + + "00000naa a2200000uc 4500" + XML_MARC_COLLECTION_END_TAG; + String actual = resultCollector.toString(); + assertNotEquals(expected, actual); + } + @Test public void issue527_shouldEmitLeaderAlwaysAsWholeString() { createRecordWithLeader("1", "a", "o", "a", " ", "a", "z", "u", " ");