3 import java.io.IOException;
5 import java.util.Iterator;
8 import javax.xml.transform.OutputKeys;
9 import javax.xml.transform.Result;
10 import javax.xml.transform.Source;
11 import javax.xml.transform.TransformerFactory;
12 import javax.xml.transform.sax.SAXTransformerFactory;
13 import javax.xml.transform.sax.TransformerHandler;
15 import org.marc4j.converter.CharConverter;
16 import org.marc4j.marc.ControlField;
17 import org.marc4j.marc.DataField;
18 import org.marc4j.marc.Leader;
19 import org.marc4j.marc.Record;
20 import org.marc4j.marc.Subfield;
21 import org.xml.sax.SAXException;
22 import org.xml.sax.helpers.AttributesImpl;
24 import com.ibm.icu.text.Normalizer;
26 public class BaseMarcXmlWriter implements MarcWriter {
29 * Character encoding. Default is UTF-8.
31 protected CharConverter converter = null;
32 protected Writer writer = null;
33 protected boolean indent = false;
34 protected TransformerHandler handler = null;
35 boolean normalize = false;
36 private String namespaceURI;
37 private String collectionName;
38 private String prefix;
39 private String qualifiedCollectionName;
40 private String recordName;
41 private String qualifiedRecordName;
42 private String leaderName;
43 private String qualifiedLeaderName;
44 private String subfieldTemplate;
45 private String qualifiedSubfieldTemplate;
46 private String datafieldTemplate;
47 private String qualifiedDatafieldTemplate;
48 private String controlfieldTemplate;
49 private String qualifiedControlfieldTemplate;
50 private boolean useTurboMarc = true;
53 * Returns the character converter.
55 * @return CharConverter the character converter
57 public CharConverter getConverter() {
62 * Sets the character converter.
65 * the character converter
67 public void setConverter(CharConverter converter) {
68 this.converter = converter;
72 * Writes a Record object to the result.
75 * the <code>Record</code> object
76 * @throws SAXException
78 public void write(Record record) {
81 } catch (SAXException e) {
82 throw new MarcException("SAX error occured while writing record", e);
91 } catch (IOException e) {
92 throw new MarcException(e.getMessage(), e);
97 * If set to true this writer will perform Unicode normalization on data
98 * elements using normalization form C (NFC). The default is false.
100 * The implementation used is ICU4J 2.6. This version is based on Unicode
104 * true if this writer performs Unicode normalization, false
107 public void setUnicodeNormalization(boolean normalize) {
108 this.normalize = normalize;
112 * Returns true if this writer will perform Unicode normalization, false
115 * @return boolean - true if this writer performs Unicode normalization,
118 public boolean getUnicodeNormalization() {
122 protected void setHandler(Result result, Source stylesheet) throws MarcException {
124 TransformerFactory factory = TransformerFactory.newInstance();
125 if (!factory.getFeature(SAXTransformerFactory.FEATURE))
126 throw new UnsupportedOperationException(
127 "SAXTransformerFactory is not supported");
129 SAXTransformerFactory saxFactory = (SAXTransformerFactory) factory;
130 if (stylesheet == null)
131 handler = saxFactory.newTransformerHandler();
133 handler = saxFactory.newTransformerHandler(stylesheet);
134 handler.getTransformer()
135 .setOutputProperty(OutputKeys.METHOD, "xml");
136 handler.setResult(result);
138 } catch (Exception e) {
139 throw new MarcException(e.getMessage(), e);
144 * Writes the root start tag to the result.
146 * @throws SAXException
148 protected void writeStartDocument() {
150 AttributesImpl atts = new AttributesImpl();
151 handler.startDocument();
152 // Add a new line after <?xml ?>
153 handler.ignorableWhitespace("\n".toCharArray(), 0, 1);
154 // The next line duplicates the namespace declaration for Marc XML
155 handler.startPrefixMapping(prefix, namespaceURI);
156 // add namespace declaration using attribute - need better solution
157 //atts.addAttribute(namespaceURI, "xmlns", "xmlns:" + prefix, "CDATA", namespaceURI);
158 handler.startElement(namespaceURI, collectionName, qualifiedCollectionName, atts);
159 } catch (SAXException e) {
160 throw new MarcException(
161 "SAX error occured while writing start document", e);
166 * Writes the root end tag to the result.
168 * @throws SAXException
170 protected void writeEndDocument() {
173 handler.ignorableWhitespace("\n".toCharArray(), 0, 1);
175 handler.endElement(namespaceURI, collectionName, qualifiedCollectionName);
176 handler.endPrefixMapping("");
177 handler.endDocument();
178 } catch (SAXException e) {
179 throw new MarcException(
180 "SAX error occured while writing end document", e);
185 * Returns true if indentation is active, false otherwise.
189 public boolean hasIndent() {
194 * Activates or deactivates indentation. Default value is false.
198 public void setIndent(boolean indent) {
199 this.indent = indent;
202 protected char[] getDataElement(String data) {
203 String dataElement = null;
204 if (converter == null)
205 return data.toCharArray();
206 dataElement = converter.convert(data);
208 dataElement = Normalizer.normalize(dataElement, Normalizer.NFC);
209 return dataElement.toCharArray();
212 public String getNamespaceURI() {
216 public void setNamespaceURI(String namespaceURI) {
217 this.namespaceURI = namespaceURI;
220 public String getCollectionName() {
221 return collectionName;
224 public void setCollectionName(String collectionName) {
225 this.collectionName = collectionName;
226 qualifiedCollectionName = setPrefixedName(collectionName);
229 private String setPrefixedName(String name) {
230 if (prefix != null && name != null)
231 return prefix + name;
235 protected void handleDataField(DataField field) throws SAXException {
236 AttributesImpl atts = new AttributesImpl();
238 atts.addAttribute("", "tag", "tag", "CDATA", field.getTag());
239 atts.addAttribute("", "ind1", "ind1", "CDATA", String.valueOf(field.getIndicator1()));
240 atts.addAttribute("", "ind2", "ind2", "CDATA", String.valueOf(field.getIndicator2()));
243 handler.ignorableWhitespace("\n ".toCharArray(), 0, 5);
244 StringBuffer elementName = new StringBuffer(datafieldTemplate);
245 StringBuffer qElementName = new StringBuffer(qualifiedDatafieldTemplate);
247 elementName.append(field.getTag());
248 qElementName.append(field.getTag());
250 handler.startElement(namespaceURI, elementName.toString(), qElementName.toString(), atts);
252 handleSubfields(field.getSubfields());
254 handler.ignorableWhitespace("\n ".toCharArray(), 0, 5);
255 handler.endElement(namespaceURI, elementName.toString(), qElementName.toString());
258 protected void handleSubfields(List<Subfield> subfields) throws SAXException {
259 Iterator<Subfield> si = subfields.iterator();
260 while (si.hasNext()) {
261 Subfield subfield = (Subfield) si.next();
262 handleSubfield(subfield);
266 protected void handleSubfield(Subfield subfield) throws SAXException {
267 AttributesImpl atts = new AttributesImpl();
268 StringBuffer subfieldName = new StringBuffer(subfieldTemplate);
269 StringBuffer qSubfieldName = new StringBuffer(qualifiedSubfieldTemplate);
271 char code = subfield.getCode();
272 // if [a-zA-Z0-9] append to elementName, otherwise use a attribute
273 if (code >= '0' && code <= '9' ||
274 code >= 'a' && code <= 'z' ||
275 code >= 'A' && code <= 'Z') {
276 subfieldName.append(code);
277 qSubfieldName.append(code);
280 atts = new AttributesImpl();
281 atts.addAttribute("", "code", "code", "CDATA", String
282 .valueOf(subfield.getCode()));
285 handler.ignorableWhitespace("\n ".toCharArray(), 0, 7);
287 handler.startElement(namespaceURI, subfieldName.toString(), qSubfieldName.toString(), atts);
288 char[] temp = getDataElement(subfield.getData());
289 handler.characters(temp, 0, temp.length);
290 handler.endElement(namespaceURI, subfieldName.toString(), qSubfieldName.toString());
293 protected void toXml(Record record) throws SAXException {
294 AttributesImpl atts = new AttributesImpl();
296 handler.ignorableWhitespace("\n ".toCharArray(), 0, 3);
298 handler.startElement(namespaceURI, recordName, qualifiedRecordName, atts);
301 handler.ignorableWhitespace("\n ".toCharArray(), 0, 5);
303 handleLeader(record, atts);
305 handleControlfields(record.getControlFields());
306 Iterator<DataField> di = record.getDataFields().iterator();
307 while (di.hasNext()) {
308 DataField field = di.next();
309 handleDataField(field);
313 handler.ignorableWhitespace("\n ".toCharArray(), 0, 3);
315 handler.endElement(namespaceURI, recordName, qualifiedRecordName);
318 protected void handleControlfields(List<ControlField> controlFields) throws SAXException {
319 Iterator<ControlField> ci = controlFields.iterator();
320 while (ci.hasNext()) {
321 ControlField field = (ControlField) ci.next();
322 handleControlField(field);
326 protected void handleLeader(Record record, AttributesImpl atts) throws SAXException {
328 handler.startElement(namespaceURI, leaderName, qualifiedLeaderName, atts);
329 Leader leader = record.getLeader();
330 temp = leader.toString().toCharArray();
331 handler.characters(temp, 0, temp.length);
332 handler.endElement(namespaceURI, leaderName, qualifiedLeaderName);
335 protected void handleControlField(ControlField field) throws SAXException {
336 AttributesImpl atts = new AttributesImpl();
339 atts.addAttribute("", "tag", "tag", "CDATA", field.getTag());
342 handler.ignorableWhitespace("\n ".toCharArray(), 0, 5);
343 StringBuffer elementName = new StringBuffer(controlfieldTemplate);
344 StringBuffer qElementName = new StringBuffer(qualifiedControlfieldTemplate);
346 elementName.append(field.getTag());
347 qElementName.append(field.getTag());
349 handler.startElement(namespaceURI, elementName.toString(), qElementName.toString(), atts);
350 char[] temp = getDataElement(field.getData());
351 handler.characters(temp, 0, temp.length);
352 handler.endElement(namespaceURI, elementName.toString(), qElementName.toString());
355 public String getPrefix() {
359 public void setPrefix(String prefix) {
360 this.prefix = prefix;
361 // Update the prefixed names
362 qualifiedCollectionName = setPrefixedName(collectionName);
363 qualifiedRecordName = setPrefixedName(recordName);
364 qualifiedControlfieldTemplate = setPrefixedName(controlfieldTemplate);
365 qualifiedLeaderName = setPrefixedName(leaderName);
366 qualifiedDatafieldTemplate = setPrefixedName(datafieldTemplate);
367 qualifiedDatafieldTemplate = setPrefixedName(datafieldTemplate);
368 qualifiedSubfieldTemplate = setPrefixedName(subfieldTemplate);
371 public String getRecordName() {
375 public void setRecordName(String recordName) {
376 this.recordName = recordName;
377 qualifiedRecordName = setPrefixedName(recordName);
380 public String getLeaderName() {
384 public void setLeaderName(String leaderName) {
385 this.leaderName = leaderName;
386 qualifiedLeaderName = setPrefixedName(leaderName);
389 public String getSubfieldTemplate() {
390 return subfieldTemplate;
393 public void setSubfieldTemplate(String subfieldTemplate) {
394 this.subfieldTemplate = subfieldTemplate;
395 qualifiedSubfieldTemplate = setPrefixedName(subfieldTemplate);
398 public String getDatafieldTemplate() {
399 return datafieldTemplate;
402 public void setDatafieldTemplate(String datafieldTemplate) {
403 this.datafieldTemplate = datafieldTemplate;
404 qualifiedDatafieldTemplate = setPrefixedName(datafieldTemplate);
407 public String getControlfieldTemplate() {
408 return controlfieldTemplate;
411 public void setControlfieldTemplate(String controlfieldTemplate) {
412 this.controlfieldTemplate = controlfieldTemplate;
413 qualifiedControlfieldTemplate = setPrefixedName(controlfieldTemplate);
416 public boolean isUseTurboMarc() {
420 public void setUseTurboMarc(boolean useTurboMarc) {
421 this.useTurboMarc = useTurboMarc;