2525import org .metafacture .framework .annotations .Out ;
2626import org .metafacture .framework .helpers .DefaultStreamPipe ;
2727
28+ import java .util .Arrays ;
2829import java .util .Collections ;
30+ import java .util .function .Function ;
2931
3032/**
3133 * Encodes a stream into MARCXML.
@@ -48,25 +50,48 @@ public final class MarcXmlEncoder extends DefaultStreamPipe<ObjectReceiver<Strin
4850 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\" >" ;
4951 private static final String ROOT_CLOSE = "</marc:collection>" ;
5052
51- private static final String RECORD_OPEN = "<marc:record>" ;
52- private static final String RECORD_CLOSE = "</marc:record>" ;
53+ private enum Tag {
5354
54- private static final String ATTRIBUTE_TEMPLATE = " %s=\" %s\" " ;
55+ collection (" xmlns%s=\" " + NAMESPACE + "\" %s" ),
56+ controlfield (" tag=\" %s\" " ),
57+ datafield (" tag=\" %s\" ind1=\" %s\" ind2=\" %s\" " ),
58+ leader ("" ),
59+ record ("" ),
60+ subfield (" code=\" %s\" " );
61+
62+ private static final String OPEN_TEMPLATE = "<%%s%s%s>" ;
63+ private static final String CLOSE_TEMPLATE = "</%%s%s>" ;
64+
65+ private final String openTemplate ;
66+ private final String closeTemplate ;
67+
68+ Tag (final String template ) {
69+ openTemplate = String .format (OPEN_TEMPLATE , name (), template );
70+ closeTemplate = String .format (CLOSE_TEMPLATE , name ());
71+ }
72+
73+ public String open (final Object [] args ) {
74+ return String .format (openTemplate , args );
75+ }
76+
77+ public String close (final Object [] args ) {
78+ return String .format (closeTemplate , args );
79+ }
5580
56- private static final String CONTROLFIELD_OPEN_TEMPLATE = "<marc:controlfield tag=\" %s\" >" ;
57- private static final String CONTROLFIELD_CLOSE = "</marc:controlfield>" ;
81+ }
5882
59- private static final String DATAFIELD_OPEN_TEMPLATE = "<marc:datafield tag=\" %s\" ind1=\" %s\" ind2=\" %s\" >" ;
60- private static final String DATAFIELD_CLOSE = "</marc:datafield>" ;
83+ private static final String NAMESPACE = "http://www.loc.gov/MARC21/slim" ;
84+ private static final String NAMESPACE_NAME = "marc" ;
85+ private static final String NAMESPACE_PREFIX = NAMESPACE_NAME + ":" ;
86+ private static final String NAMESPACE_SUFFIX = ":" + NAMESPACE_NAME ;
6187
62- private static final String SUBFIELD_OPEN_TEMPLATE = "<marc:subfield code=\" %s\" >" ;
63- private static final String SUBFIELD_CLOSE = "</marc:subfield>" ;
88+ 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\" " ;
6489
65- private static final String LEADER_OPEN_TEMPLATE = "<marc:leader>" ;
66- private static final String LEADER_CLOSE_TEMPLATE = "</marc:leader>" ;
90+ private static final String ATTRIBUTE_TEMPLATE = " %s=\" %s\" " ;
6791
6892 private static final String NEW_LINE = "\n " ;
6993 private static final String INDENT = "\t " ;
94+ private static final String EMPTY = "" ;
7095
7196 private static final String XML_DECLARATION_TEMPLATE = "<?xml version=\" %s\" encoding=\" %s\" ?>" ;
7297
@@ -89,6 +114,9 @@ public final class MarcXmlEncoder extends DefaultStreamPipe<ObjectReceiver<Strin
89114
90115 private String currentEntity = "" ;
91116
117+ private boolean emitNamespace = true ;
118+ private Object [] namespacePrefix = new Object []{emitNamespace ? NAMESPACE_PREFIX : EMPTY };
119+
92120 private int indentationLevel ;
93121 private boolean formatted = PRETTY_PRINTED ;
94122 private int recordAttributeOffset ;
@@ -99,6 +127,11 @@ public final class MarcXmlEncoder extends DefaultStreamPipe<ObjectReceiver<Strin
99127 public MarcXmlEncoder () {
100128 }
101129
130+ public void setEmitNamespace (final boolean emitNamespace ) {
131+ this .emitNamespace = emitNamespace ;
132+ namespacePrefix = new Object []{emitNamespace ? NAMESPACE_PREFIX : EMPTY };
133+ }
134+
102135 /**
103136 * Sets the flag to decide whether to omit the XML declaration.
104137 *
@@ -151,14 +184,14 @@ public void startRecord(final String identifier) {
151184 writeHeader ();
152185 prettyPrintNewLine ();
153186 }
154- writeRaw ( ROOT_OPEN );
187+ writeTag ( Tag . collection :: open , emitNamespace ? NAMESPACE_SUFFIX : EMPTY , emitNamespace ? SCHEMA_ATTRIBUTES : EMPTY );
155188 prettyPrintNewLine ();
156189 incrementIndentationLevel ();
157190 }
158191 atStreamStart = false ;
159192
160193 prettyPrintIndentation ();
161- writeRaw ( RECORD_OPEN );
194+ writeTag ( Tag . record :: open );
162195 recordAttributeOffset = builder .length () - 1 ;
163196 prettyPrintNewLine ();
164197
@@ -169,7 +202,7 @@ public void startRecord(final String identifier) {
169202 public void endRecord () {
170203 decrementIndentationLevel ();
171204 prettyPrintIndentation ();
172- writeRaw ( RECORD_CLOSE );
205+ writeTag ( Tag . record :: close );
173206 prettyPrintNewLine ();
174207 sendAndClearData ();
175208 }
@@ -188,7 +221,7 @@ public void startEntity(final String name) {
188221 final String ind1 = name .substring (IND1_BEGIN , IND1_END );
189222 final String ind2 = name .substring (IND2_BEGIN , IND2_END );
190223 prettyPrintIndentation ();
191- writeRaw ( String . format ( DATAFIELD_OPEN_TEMPLATE , tag , ind1 , ind2 ) );
224+ writeTag ( Tag . datafield :: open , tag , ind1 , ind2 );
192225 prettyPrintNewLine ();
193226 incrementIndentationLevel ();
194227 }
@@ -199,7 +232,7 @@ public void endEntity() {
199232 if (!currentEntity .equals (Marc21EventNames .LEADER_ENTITY )) {
200233 decrementIndentationLevel ();
201234 prettyPrintIndentation ();
202- writeRaw ( DATAFIELD_CLOSE );
235+ writeTag ( Tag . datafield :: close );
203236 prettyPrintNewLine ();
204237 }
205238 currentEntity = "" ;
@@ -215,19 +248,19 @@ public void literal(final String name, final String value) {
215248 }
216249 else if (!writeLeader (name , value )) {
217250 prettyPrintIndentation ();
218- writeRaw ( String . format ( CONTROLFIELD_OPEN_TEMPLATE , name ) );
251+ writeTag ( Tag . controlfield :: open , name );
219252 if (value != null ) {
220253 writeEscaped (value .trim ());
221254 }
222- writeRaw ( CONTROLFIELD_CLOSE );
255+ writeTag ( Tag . controlfield :: close );
223256 prettyPrintNewLine ();
224257 }
225258 }
226259 else if (!writeLeader (currentEntity , value )) {
227260 prettyPrintIndentation ();
228- writeRaw ( String . format ( SUBFIELD_OPEN_TEMPLATE , name ) );
261+ writeTag ( Tag . subfield :: open , name );
229262 writeEscaped (value .trim ());
230- writeRaw ( SUBFIELD_CLOSE );
263+ writeTag ( Tag . subfield :: close );
231264 prettyPrintNewLine ();
232265 }
233266 }
@@ -264,7 +297,7 @@ private void writeHeader() {
264297
265298 /** Closes the root tag */
266299 private void writeFooter () {
267- writeRaw ( ROOT_CLOSE );
300+ writeTag ( Tag . collection :: close );
268301 }
269302
270303 /**
@@ -288,7 +321,9 @@ private void writeEscaped(final String str) {
288321 private boolean writeLeader (final String name , final String value ) {
289322 if (name .equals (Marc21EventNames .LEADER_ENTITY )) {
290323 prettyPrintIndentation ();
291- writeRaw (LEADER_OPEN_TEMPLATE + value + LEADER_CLOSE_TEMPLATE );
324+ writeTag (Tag .leader ::open );
325+ writeRaw (value );
326+ writeTag (Tag .leader ::close );
292327 prettyPrintNewLine ();
293328
294329 return true ;
@@ -298,6 +333,12 @@ private boolean writeLeader(final String name, final String value) {
298333 }
299334 }
300335
336+ private void writeTag (final Function <Object [], String > function , final Object ... args ) {
337+ final Object [] allArgs = Arrays .copyOf (namespacePrefix , namespacePrefix .length + args .length );
338+ System .arraycopy (args , 0 , allArgs , namespacePrefix .length , args .length );
339+ writeRaw (function .apply (allArgs ));
340+ }
341+
301342 private void prettyPrintIndentation () {
302343 if (formatted ) {
303344 final String prefix = String .join ("" , Collections .nCopies (indentationLevel , INDENT ));
0 commit comments