-
Notifications
You must be signed in to change notification settings - Fork 0
/
pica.js
150 lines (126 loc) · 4.43 KB
/
pica.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/**
* PICA data processing module.
*/
// parsing and serializing PICA+ <-> PICA/JSON
export const picaFieldIdentifier = field => {
if (typeof field !== "object") return
const [tag, occ] = Array.isArray(field) ? field : [ field.tag, field.occurrence ]
const counter = Array.isArray(field) ? null : field.counter
return tag
+ (counter ? "x" + counter : "")
+ (occ ? "/" + (occ.length === 1 ? "0" + occ : occ) : "")
}
function subfieldValue(field, code) {
for(var i=2; i<field.length; i+=2) {
if (field[i] === code) return field[i+1]
}
}
export const serializePicaField = field =>
picaFieldIdentifier(field)
+ " "
+ field.slice(2).map((s,i) => i % 2 ? s.replace(/\$/g,"$$$") : "$" + s).join("")
export const serializePica = pica =>
pica.map(serializePicaField).join("\n")
const picaSubfieldPattern = /\$([A-Za-z0-9])((?:[^$]+|\$\$)+)/g
const picaLinePattern = new RegExp([
/^([012][0-9][0-9][A-Z@])/,
/(\/([0-9]{2,3}))?/,
/\s*/,
/((\$([A-Za-z0-9])([^$]|\$\$)+)+)$/,
].map(r => r.source).join(""))
export const parsePicaLine = line => {
const match = line.match(picaLinePattern)
if (match) {
const tag = match[1]
const occurrence = match[3]
const subfields = match[4]
const field = [ tag, occurrence ]
for (let m of subfields.matchAll(picaSubfieldPattern)) {
field.push(m[1], m[2].replace(/\$\$/g, "$"))
}
return field
}
}
export const parsePica = text => text.split(/\n/).map(parsePicaLine).filter(Boolean)
import { PicaPath } from "./picapath.js"
export { PicaPath } from "./picapath.js"
// filter record to fields listed in an array of PICA path expressions
export const filterPicaFields = (pica, exprs) => {
exprs = Array.isArray(exprs) ? exprs : exprs.split(/\|/)
exprs = exprs.map(e => e instanceof PicaPath ? e : new PicaPath(e))
return pica.filter(field => exprs.some(e => e.matchField(field)))
}
// get PPN of a record
const PPN = new PicaPath("003@$0")
export const getPPN = record => PPN.getValues(record)[0]
export const picaFieldScheduleIdentifier = (schema, field) => {
if (!schema || !field) return
var [tag, occ] = Array.isArray(field) ? field : field.split("/")
var counter
if (tag && tag.charAt(0) === "2") {
occ = null
counter = subfieldValue(field, "x")
}
const id = picaFieldIdentifier(counter ? {tag, occurrence: occ, counter} : [tag, occ])
const fields = schema.fields || {}
if (id in fields) {
return id
} else if (occ) {
// occurrence may be in a range
occ = Number(occ)
return Object.keys(fields).find(key => {
const [t, from, to] = key.split(/[/-]/)
return t === tag && to && Number(from) <= occ && occ <= Number(to)
})
} else if (counter) {
// counter may be in a range
counter = Number(counter)
return Object.keys(fields).find(key => {
const m = key.match(/^(2...)x(.+)-(.+)$/)
return m && tag === m[1] && Number(m[2]) <= counter && counter <= Number(m[3])
})
}
}
export const picaFieldSchedule = (schema, field) => {
const id = picaFieldScheduleIdentifier(schema, field)
return id && schema.fields ? schema.fields[id] : undefined
}
// experimental: serialize
export const serializePica3Subfield = (code, value, schedule) => {
const { pica3 } = schedule || {}
if (pica3 === undefined) return
if (pica3.match(/[.]{3}/)) {
return pica3.replace("...", value)
} else {
return pica3 + value
}
}
export const serializePica3Field = (field, schedule) => {
var { pica3, subfields } = schedule
// TODO: support occurrences and field ranges
pica3 += " "
for (let i=2; i<field.length; i+=2) {
pica3 += serializePica3Subfield(field[i], field[i+1], subfields[field[i]])
}
return pica3
}
export const serializePica3 = (record, schema) => {
if (!schema || !schema.fields) return
return record.map(field => serializePica3Field(field, picaFieldSchedule(schema, field))).join("\n")
}
export const reduceRecord = (record, schema) => {
return record.filter(field => picaFieldScheduleIdentifier(schema, field))
}
export const ppnChecksum = ppn => {
if (ppn && ppn.match("^[0-9]+$")) {
const length = ppn.length
let sum = 0
for (let i=0; i<length; i++) {
sum += ppn[i] * (length-i+1)
}
sum = (11 - (sum % 11)) % 11
return sum === 10 ? "X" : sum
}
}
export const isDbkey = dbkey => dbkey && dbkey.match("^[a-z][a-z0-9-]*[a-z0-9]$")
export const isPPN = ppn => ppn && ppn.match("^[0-9][0-9]+X?$") && ppn.slice(-1) == ppnChecksum(ppn.slice(0,-1))