Skip to content

Commit 533b5c3

Browse files
authored
Merge pull request #410 from metafacture/403-makeMarcXmlEncoderNamespaceConfigurable
Make emitting MARCXML namespace configurable.
2 parents 7993b37 + ef9bbb1 commit 533b5c3

File tree

2 files changed

+76
-24
lines changed

2 files changed

+76
-24
lines changed

metafacture-biblio/src/main/java/org/metafacture/biblio/marc21/MarcXmlEncoder.java

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
import org.metafacture.framework.annotations.Out;
2626
import org.metafacture.framework.helpers.DefaultStreamPipe;
2727

28+
import java.util.Arrays;
2829
import java.util.Collections;
30+
import java.util.function.Function;
2931

3032
/**
3133
* Encodes a stream into MARCXML.
@@ -40,28 +42,48 @@
4042
@FluxCommand("encode-marcxml")
4143
public final class MarcXmlEncoder extends DefaultStreamPipe<ObjectReceiver<String>> {
4244

43-
private static final String ROOT_OPEN = "<marc:collection xmlns:marc=\"http://www.loc.gov/MARC21/slim\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.loc.gov/MARC21/slim http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\">";
44-
private static final String ROOT_CLOSE = "</marc:collection>";
45+
private enum Tag {
4546

46-
private static final String RECORD_OPEN = "<marc:record>";
47-
private static final String RECORD_CLOSE = "</marc:record>";
47+
collection(" xmlns%s=\"" + NAMESPACE + "\"%s"),
48+
controlfield(" tag=\"%s\""),
49+
datafield(" tag=\"%s\" ind1=\"%s\" ind2=\"%s\""),
50+
leader(""),
51+
record(""),
52+
subfield(" code=\"%s\"");
4853

49-
private static final String ATTRIBUTE_TEMPLATE = " %s=\"%s\"";
54+
private static final String OPEN_TEMPLATE = "<%%s%s%s>";
55+
private static final String CLOSE_TEMPLATE = "</%%s%s>";
56+
57+
private final String openTemplate;
58+
private final String closeTemplate;
59+
60+
Tag(final String template) {
61+
openTemplate = String.format(OPEN_TEMPLATE, name(), template);
62+
closeTemplate = String.format(CLOSE_TEMPLATE, name());
63+
}
64+
65+
public String open(final Object[] args) {
66+
return String.format(openTemplate, args);
67+
}
68+
69+
public String close(final Object[] args) {
70+
return String.format(closeTemplate, args);
71+
}
5072

51-
private static final String CONTROLFIELD_OPEN_TEMPLATE = "<marc:controlfield tag=\"%s\">";
52-
private static final String CONTROLFIELD_CLOSE = "</marc:controlfield>";
73+
}
5374

54-
private static final String DATAFIELD_OPEN_TEMPLATE = "<marc:datafield tag=\"%s\" ind1=\"%s\" ind2=\"%s\">";
55-
private static final String DATAFIELD_CLOSE = "</marc:datafield>";
75+
private static final String NAMESPACE = "http://www.loc.gov/MARC21/slim";
76+
private static final String NAMESPACE_NAME = "marc";
77+
private static final String NAMESPACE_PREFIX = NAMESPACE_NAME + ":";
78+
private static final String NAMESPACE_SUFFIX = ":" + NAMESPACE_NAME;
5679

57-
private static final String SUBFIELD_OPEN_TEMPLATE = "<marc:subfield code=\"%s\">";
58-
private static final String SUBFIELD_CLOSE = "</marc:subfield>";
80+
private static final String SCHEMA_ATTRIBUTES = " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"" + NAMESPACE + " http://www.loc.gov/standards/marcxml/schema/MARC21slim.xsd\"";
5981

60-
private static final String LEADER_OPEN_TEMPLATE = "<marc:leader>";
61-
private static final String LEADER_CLOSE_TEMPLATE = "</marc:leader>";
82+
private static final String ATTRIBUTE_TEMPLATE = " %s=\"%s\"";
6283

6384
private static final String NEW_LINE = "\n";
6485
private static final String INDENT = "\t";
86+
private static final String EMPTY = "";
6587

6688
private static final String XML_DECLARATION_TEMPLATE = "<?xml version=\"%s\" encoding=\"%s\"?>";
6789

@@ -82,6 +104,9 @@ public final class MarcXmlEncoder extends DefaultStreamPipe<ObjectReceiver<Strin
82104
private String xmlVersion;
83105
private String xmlEncoding;
84106

107+
private boolean emitNamespace;
108+
private Object[] namespacePrefix;
109+
85110
private String currentEntity;
86111
private int indentationLevel;
87112
private int recordAttributeOffset;
@@ -99,6 +124,13 @@ public MarcXmlEncoder() {
99124

100125
this.indentationLevel = 0;
101126
this.formatted = true;
127+
128+
setEmitNamespace(true);
129+
}
130+
131+
public void setEmitNamespace(final boolean emitNamespace) {
132+
this.emitNamespace = emitNamespace;
133+
namespacePrefix = new Object[]{emitNamespace ? NAMESPACE_PREFIX : EMPTY};
102134
}
103135

104136
public void omitXmlDeclaration(final boolean currentOmitXmlDeclaration) {
@@ -130,14 +162,14 @@ public void startRecord(final String identifier) {
130162
writeHeader();
131163
prettyPrintNewLine();
132164
}
133-
writeRaw(ROOT_OPEN);
165+
writeTag(Tag.collection::open, emitNamespace ? NAMESPACE_SUFFIX : EMPTY, emitNamespace ? SCHEMA_ATTRIBUTES : EMPTY);
134166
prettyPrintNewLine();
135167
incrementIndentationLevel();
136168
}
137169
atStreamStart = false;
138170

139171
prettyPrintIndentation();
140-
writeRaw(RECORD_OPEN);
172+
writeTag(Tag.record::open);
141173
recordAttributeOffset = builder.length() - 1;
142174
prettyPrintNewLine();
143175

@@ -148,7 +180,7 @@ public void startRecord(final String identifier) {
148180
public void endRecord() {
149181
decrementIndentationLevel();
150182
prettyPrintIndentation();
151-
writeRaw(RECORD_CLOSE);
183+
writeTag(Tag.record::close);
152184
prettyPrintNewLine();
153185
sendAndClearData();
154186
}
@@ -167,7 +199,7 @@ public void startEntity(final String name) {
167199
final String ind1 = name.substring(IND1_BEGIN, IND1_END);
168200
final String ind2 = name.substring(IND2_BEGIN, IND2_END);
169201
prettyPrintIndentation();
170-
writeRaw(String.format(DATAFIELD_OPEN_TEMPLATE, tag, ind1, ind2));
202+
writeTag(Tag.datafield::open, tag, ind1, ind2);
171203
prettyPrintNewLine();
172204
incrementIndentationLevel();
173205
}
@@ -178,7 +210,7 @@ public void endEntity() {
178210
if (!currentEntity.equals(Marc21EventNames.LEADER_ENTITY)) {
179211
decrementIndentationLevel();
180212
prettyPrintIndentation();
181-
writeRaw(DATAFIELD_CLOSE);
213+
writeTag(Tag.datafield::close);
182214
prettyPrintNewLine();
183215
}
184216
currentEntity = "";
@@ -194,19 +226,19 @@ public void literal(final String name, final String value) {
194226
}
195227
else if (!writeLeader(name, value)) {
196228
prettyPrintIndentation();
197-
writeRaw(String.format(CONTROLFIELD_OPEN_TEMPLATE, name));
229+
writeTag(Tag.controlfield::open, name);
198230
if (value != null) {
199231
writeEscaped(value.trim());
200232
}
201-
writeRaw(CONTROLFIELD_CLOSE);
233+
writeTag(Tag.controlfield::close);
202234
prettyPrintNewLine();
203235
}
204236
}
205237
else if (!writeLeader(currentEntity, value)) {
206238
prettyPrintIndentation();
207-
writeRaw(String.format(SUBFIELD_OPEN_TEMPLATE, name));
239+
writeTag(Tag.subfield::open, name);
208240
writeEscaped(value.trim());
209-
writeRaw(SUBFIELD_CLOSE);
241+
writeTag(Tag.subfield::close);
210242
prettyPrintNewLine();
211243
}
212244
}
@@ -243,7 +275,7 @@ private void writeHeader() {
243275

244276
/** Closes the root tag */
245277
private void writeFooter() {
246-
writeRaw(ROOT_CLOSE);
278+
writeTag(Tag.collection::close);
247279
}
248280

249281
/** Writes a unescaped sequence */
@@ -259,7 +291,9 @@ private void writeEscaped(final String str) {
259291
private boolean writeLeader(final String name, final String value) {
260292
if (name.equals(Marc21EventNames.LEADER_ENTITY)) {
261293
prettyPrintIndentation();
262-
writeRaw(LEADER_OPEN_TEMPLATE + value + LEADER_CLOSE_TEMPLATE);
294+
writeTag(Tag.leader::open);
295+
writeRaw(value);
296+
writeTag(Tag.leader::close);
263297
prettyPrintNewLine();
264298

265299
return true;
@@ -269,6 +303,12 @@ private boolean writeLeader(final String name, final String value) {
269303
}
270304
}
271305

306+
private void writeTag(final Function<Object[], String> function, final Object... args) {
307+
final Object[] allArgs = Arrays.copyOf(namespacePrefix, namespacePrefix.length + args.length);
308+
System.arraycopy(args, 0, allArgs, namespacePrefix.length, args.length);
309+
writeRaw(function.apply(allArgs));
310+
}
311+
272312
private void prettyPrintIndentation() {
273313
if (formatted) {
274314
final String prefix = String.join("", Collections.nCopies(indentationLevel, INDENT));

metafacture-biblio/src/test/java/org/metafacture/biblio/marc21/MarcXmlEncoderTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,18 @@ public void createTwoRecordsInOneCollection() {
168168
assertEquals(expected, actual);
169169
}
170170

171+
@Test
172+
public void issue403_shouldNotEmitNamespaceIfDisabled() {
173+
encoder.setEmitNamespace(false);
174+
addOneRecord(encoder);
175+
addOneRecord(encoder);
176+
encoder.closeStream();
177+
String expected = XML_DECLARATION + "<collection xmlns=\"http://www.loc.gov/MARC21/slim\">"
178+
+ XML_RECORD + XML_RECORD + XML_MARC_COLLECTION_END_TAG;
179+
String actual = resultCollector.toString();
180+
assertEquals(expected.replace("marc:", ""), actual);
181+
}
182+
171183
@Test(expected = MetafactureException.class)
172184
public void emitExceptionWhenEntityLengthNot5() {
173185
encoder.startRecord(RECORD_ID);

0 commit comments

Comments
 (0)