1 | var fs = require('fs');
|
2 | var util = require('util');
|
3 |
|
4 | var iconv = null;
|
5 | try {
|
6 | iconv = require('iconv-lite');
|
7 | } catch (err) {
|
8 |
|
9 | iconv = null;
|
10 | }
|
11 |
|
12 | var bufferWrapper = require('./bufferwrapper');
|
13 | var MarcRecord = require('./record').MarcRecord;
|
14 |
|
15 | var field = require('./field');
|
16 | var MarcVariableField = field.MarcVariableField;
|
17 | var MarcControlField = field.MarcControlField;
|
18 | var MarcDataField = field.MarcDataField;
|
19 | var MarcSubfield = field.MarcSubfield;
|
20 |
|
21 |
|
22 |
|
23 |
|
24 | function MarcXmlWriter(options) {
|
25 | if (!(this instanceof MarcXmlWriter)) {
|
26 | return new MarcXmlWriter(options);
|
27 | }
|
28 |
|
29 |
|
30 | this.recordsFile = null;
|
31 |
|
32 | this.readyToWrite = false;
|
33 |
|
34 | this.position = null;
|
35 |
|
36 |
|
37 | options = options || {};
|
38 | this.options = {
|
39 |
|
40 | format: (options.format || 'UNIMARC').toUpperCase(),
|
41 |
|
42 | encoding: options.encoding || null,
|
43 |
|
44 | bom: options.bom || false
|
45 | }
|
46 | }
|
47 |
|
48 |
|
49 |
|
50 |
|
51 | MarcXmlWriter.prototype.openFile = function(recordsFile, options) {
|
52 | this.recordsFile = recordsFile;
|
53 | this.readyToWrite = true;
|
54 | this.position = 0;
|
55 |
|
56 | options = options || {};
|
57 |
|
58 | if (options.hasOwnProperty('format')) {
|
59 | this.options.format = (options.format || 'UNIMARC').toUpperCase();
|
60 | }
|
61 |
|
62 | if (options.hasOwnProperty('encoding')) {
|
63 | if (options.encoding && options.encoding !== 'utf-8'
|
64 | && iconv && iconv.encodingExists(options.encoding))
|
65 | {
|
66 | this.options.encoding = options.encoding;
|
67 | } else {
|
68 | this.options.encoding = null;
|
69 | }
|
70 | }
|
71 |
|
72 | if (options.hasOwnProperty('bom')) {
|
73 | this.options.bom = Boolean(options.bom);
|
74 | }
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 |
|
80 | MarcXmlWriter.prototype.open = function(recordsFileName) {
|
81 | var self = this;
|
82 | var options = arguments.length === 3 ? arguments[1] : undefined;
|
83 | var callback = arguments.length === 3 ? arguments[2] : arguments[1];
|
84 |
|
85 | var flags = (options || {}).flags || 'w';
|
86 | var mode = (options || {}).mode || '0666';
|
87 | fs.open(recordsFileName, flags, mode, function(err, recordsFile) {
|
88 | if (err) { return callback(err); }
|
89 | self.openFile(recordsFile, options);
|
90 |
|
91 | var header = (self.options.bom ? '\uFEFF' : '')
|
92 | + '<?xml version="1.0" encoding="'
|
93 | + (self.options.encoding || 'utf-8') + '" ?>\n';
|
94 | if (self.options.format == 'MARC21') {
|
95 | header += '<collection xmlns="http://www.loc.gov/MARC21/slim">\n';
|
96 | } else {
|
97 | header += '<collection>\n';
|
98 | }
|
99 | var buffer = self.options.encoding ?
|
100 | iconv.encode(header, self.options.encoding)
|
101 | : bufferWrapper.from(header, 'utf-8');
|
102 | fs.write(self.recordsFile, buffer, 0, buffer.length, null,
|
103 | function(err, written) {
|
104 | self.position += written;
|
105 | callback(err);
|
106 | }
|
107 | );
|
108 | });
|
109 | }
|
110 |
|
111 |
|
112 |
|
113 |
|
114 | MarcXmlWriter.prototype.openSync = function(recordsFileName, options) {
|
115 | var flags = (options || {}).flags || 'w';
|
116 | var mode = (options || {}).mode || '0666';
|
117 | var recordsFile = fs.openSync(recordsFileName, flags, mode);
|
118 | this.openFile(recordsFile, options);
|
119 |
|
120 | var header = (this.options.bom ? '\ufeff' : '')
|
121 | + '<?xml version="1.0" encoding="'
|
122 | + (this.options.encoding || 'utf-8') + '" ?>\n';
|
123 | if (this.options.format == 'MARC21') {
|
124 | header += '<collection xmlns="http://www.loc.gov/MARC21/slim">\n';
|
125 | } else {
|
126 | header += '<collection>\n';
|
127 | }
|
128 | var buffer = this.options.encoding ?
|
129 | iconv.encode(header, this.options.encoding)
|
130 | : bufferWrapper.from(header, 'utf-8');
|
131 | var written = fs.writeSync(this.recordsFile, buffer, 0,
|
132 | buffer.length, null);
|
133 | this.position += written;
|
134 | }
|
135 |
|
136 |
|
137 |
|
138 |
|
139 | MarcXmlWriter.prototype.close = function(callback) {
|
140 | var self = this;
|
141 | if (self.recordsFile !== null) {
|
142 | var footer = '</collection>\n';
|
143 | var buffer = self.options.encoding ?
|
144 | iconv.encode(footer, self.options.encoding, {addBOM: false})
|
145 | : bufferWrapper.from(footer, 'utf-8');
|
146 | fs.write(self.recordsFile, buffer, 0, buffer.length, null, function(err) {
|
147 | if (err) { return callback(err); }
|
148 | fs.close(self.recordsFile, function(err) {
|
149 | self.readyToWrite = false;
|
150 | self.recordsFile = null;
|
151 | self.position = null;
|
152 | callback(err);
|
153 | });
|
154 | });
|
155 | }
|
156 | }
|
157 |
|
158 |
|
159 |
|
160 |
|
161 | MarcXmlWriter.prototype.closeSync = function() {
|
162 | if (this.recordsFile !== null) {
|
163 | var footer = '</collection>\n';
|
164 | var buffer = this.options.encoding ?
|
165 | iconv.encode(footer, this.options.encoding, {addBOM: false})
|
166 | : bufferWrapper.from(footer, 'utf-8');
|
167 | fs.writeSync(this.recordsFile, buffer, 0, buffer.length, null);
|
168 |
|
169 | fs.closeSync(this.recordsFile);
|
170 | this.readyToWrite = false;
|
171 | this.recordsFile = null;
|
172 | this.position = null;
|
173 | }
|
174 | }
|
175 |
|
176 |
|
177 |
|
178 |
|
179 | MarcXmlWriter.prototype.write = function(record, callback) {
|
180 | var self = this;
|
181 | if (self.recordsFile === null) {
|
182 | return callback(new Error('records file must be opened'));
|
183 | }
|
184 |
|
185 | var marcXmlRecord = MarcXmlWriter.recordToMarcXml(record, self.options);
|
186 | var buffer = self.options.encoding ?
|
187 | iconv.encode(marcXmlRecord, self.options.encoding, {addBOM: false})
|
188 | : bufferWrapper.from(marcXmlRecord, 'utf-8');
|
189 | fs.write(self.recordsFile, buffer, 0, buffer.length, null,
|
190 | function(err, written) {
|
191 | self.position += written;
|
192 | callback(err);
|
193 | }
|
194 | );
|
195 | }
|
196 |
|
197 |
|
198 |
|
199 |
|
200 | MarcXmlWriter.prototype.writeSync = function(record) {
|
201 | if (this.recordsFile === null) {
|
202 | throw new Error('records file must be opened');
|
203 | }
|
204 |
|
205 | var marcXmlRecord = MarcXmlWriter.recordToMarcXml(record, this.options);
|
206 | var buffer = this.options.encoding ?
|
207 | iconv.encode(marcXmlRecord, this.options.encoding, {addBOM: false})
|
208 | : bufferWrapper.from(marcXmlRecord, 'utf-8');
|
209 | var written = fs.writeSync(this.recordsFile, buffer, 0,
|
210 | buffer.length, null);
|
211 | this.position += written;
|
212 | }
|
213 |
|
214 |
|
215 |
|
216 |
|
217 | MarcXmlWriter.prototype.getPosition = function() {
|
218 | return this.position;
|
219 | }
|
220 |
|
221 |
|
222 |
|
223 |
|
224 | MarcXmlWriter.recordToMarcXml = function(record, options) {
|
225 |
|
226 | var elements = [];
|
227 |
|
228 |
|
229 | var leader = record.getLeader();
|
230 | elements.push([1, '<record>']);
|
231 | elements.push([2, '<leader>%s</leader>', leader]);
|
232 |
|
233 |
|
234 | var fields = record.getVariableFields();
|
235 | for (var i = 0; i < fields.length; i++) {
|
236 | var field = fields[i];
|
237 | MarcXmlWriter.fieldToMarcXml(field, elements, options, 2);
|
238 | }
|
239 |
|
240 | elements.push([1, '</record>']);
|
241 |
|
242 |
|
243 | var marcXmlRecord = '';
|
244 | for (var i = 0; i < elements.length; i++) {
|
245 | var element = elements[i];
|
246 | var elementLevel = element[0];
|
247 | var elementData = element.slice(1).map(function(value, index) {
|
248 | if (index === 0) { return value; }
|
249 | return value
|
250 | .replace(/&/g, '&')
|
251 | .replace(/</g, '<')
|
252 | .replace(/>/g, '>');
|
253 | });
|
254 |
|
255 | elementString = util.format.apply(null, elementData);
|
256 | marcXmlRecord += Array(elementLevel + 1).join(' ') + elementString + '\n';
|
257 | }
|
258 |
|
259 | return marcXmlRecord;
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 |
|
265 | MarcXmlWriter.fieldToMarcXml = function(field, elements, options, level) {
|
266 | if (field.isControlField()) {
|
267 |
|
268 | elements.push([level, '<controlfield tag="%s">%s</controlfield>',
|
269 | field.tag, field.data]);
|
270 | } else {
|
271 |
|
272 | elements.push([level, '<datafield tag="%s" ind1="%s" ind2="%s">',
|
273 | field.tag, field.ind1, field.ind2]);
|
274 |
|
275 | var subfields = field.getSubfields();
|
276 | for (var i = 0; i < subfields.length; i++) {
|
277 | var subfield = subfields[i];
|
278 | MarcXmlWriter.subfieldToMarcXml(subfield, elements, options, level + 1);
|
279 | }
|
280 |
|
281 | elements.push([level, '</datafield>']);
|
282 | }
|
283 | }
|
284 |
|
285 |
|
286 |
|
287 |
|
288 | MarcXmlWriter.subfieldToMarcXml = function(subfield, elements, options,
|
289 | level)
|
290 | {
|
291 | if (!subfield.isEmbeddedField()) {
|
292 | elements.push([level, '<subfield code="%s">%s</subfield>',
|
293 | subfield.code, subfield.data]);
|
294 | return;
|
295 | }
|
296 |
|
297 | if (options.format === 'UNIMARC') {
|
298 | elements.push([level, '<s%s>', subfield.code]);
|
299 | MarcXmlWriter.fieldToMarcXml(subfield.data, elements, options, level + 1);
|
300 | elements.push([level, '</s%s>', subfield.code]);
|
301 | return;
|
302 | }
|
303 |
|
304 | var embeddedField = subfield.data;
|
305 | if (embeddedField.isControlField()) {
|
306 | elements.push([level, '<subfield code="%s">%s%s</subfield>',
|
307 | subfield.code, embeddedField.tag, embeddedField.data]);
|
308 | return;
|
309 | }
|
310 |
|
311 | elements.push([level, '<subfield code="%s">%s%s%s</subfield>',
|
312 | subfield.code, embeddedField.tag, embeddedField.ind1, embeddedField.ind2]);
|
313 | var embeddedSubfields = embeddedField.getSubfields();
|
314 | for (var i = 0; i < embeddedSubfields.length; i++) {
|
315 | var embeddedSubfield = embeddedSubfields[i];
|
316 | MarcXmlWriter.subfieldToMarcXml(
|
317 | embeddedSubfield, elements, options, level);
|
318 | }
|
319 | }
|
320 |
|
321 | module.exports = {
|
322 | MarcXmlWriter: MarcXmlWriter
|
323 | };
|