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.
4042@ FluxCommand ("encode-marcxml" )
4143public 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 ));
0 commit comments