lxml-sor.lua /size: 4547 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['lxml-sor'] = {
2    version   = 1.001,
3    comment   = "companion to lxml-sor.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local format, concat, rep = string.format, table.concat, string.rep
10local lpegmatch = lpeg.match
11local next = next
12
13local xml         = xml
14local lxml        = lxml
15local context     = context
16
17local lxmlsorters = lxml.sorters or { }
18lxml.sorters      = lxmlsorters
19
20if not lxml.splitid then
21    local splitter = lpeg.C((1-lpeg.P(":"))^1) * lpeg.P("::") * lpeg.C(lpeg.P(1)^1)
22    function lxml.splitid(id)
23        local d, i = lpegmatch(splitter,id)
24        if d then
25            return d, i
26        else
27            return "", id
28        end
29    end
30end
31
32local lists = { }
33
34function lxmlsorters.reset(name)
35    lists[name] = {
36        sorted  = false,
37        entries = { },
38        reverse = { },
39        results = { },
40    }
41end
42
43function lxmlsorters.add(name,n,key)
44    local list = lists[name]
45    if list.sorted then
46        -- reverse is messed up, we could regenerate it and go on
47    else
48        local entries = list and list.entries
49        if entries then
50            local reverse = list.reverse
51            local e = reverse[n]
52            if e then
53                local keys = entries[e][2]
54                keys[#keys+1] = key
55            else
56                entries[#entries+1] = { n, { key } }
57                reverse[n] = #entries
58            end
59        end
60    end
61end
62
63function lxmlsorters.show(name)
64    local list = lists[name]
65    local entries = list and list.entries
66    local NC, NR, bold = context.NC, context.NR, context.bold -- somehow bold is not working
67    if entries then
68        local maxn = 1
69        for i=1,#entries do
70            if #entries[i][2] > maxn then maxn = #entries[i][2] end
71        end
72        context.starttabulate { "|Tr|Tr|" .. rep("Tlp|",maxn) }
73        NC() bold("n")
74        NC() bold("id")
75        if maxn > 1 then
76            for i=1,maxn do
77                NC() bold("entry " .. i)
78            end
79        else
80            NC() bold("entry")
81        end
82        NC() NR()
83        context.HL()
84        for i=1,#entries do
85            local entry = entries[i]
86            local document, node = lxml.splitid(entry[1])
87            NC() context(i)
88            NC() context(node)
89            local e = entry[2]
90            for i=1,#e do
91                NC() context.detokenize(e[i])
92            end
93            NC() NR()
94        end
95        context.stoptabulate()
96    end
97end
98
99lxmlsorters.compare = sorters.comparers.basic -- (a,b)
100
101function lxmlsorters.sort(name)
102    local list = lists[name]
103    local entries = list and list.entries
104    if entries then
105        -- filtering
106        local results = { }
107        list.results = results
108        for i=1,#entries do
109            local entry = entries[i]
110            results[i] = {
111                entry = entry[1],
112                key = concat(entry[2], " "),
113            }
114        end
115        -- preparation
116        local strip = sorters.strip
117        local splitter = sorters.splitters.utf
118        local firstofsplit = sorters.firstofsplit
119        for i=1, #results do
120            local r = results[i]
121            r.split = splitter(strip(r.key))
122        end
123        -- sorting
124        sorters.sort(results,lxmlsorters.compare)
125        -- finalizing
126        list.nofsorted = #results
127        local split = { }
128        for k=1,#results do -- rather generic so maybe we need a function
129            local v = results[k]
130            local entry, tag = firstofsplit(v)
131            local s = split[entry] -- keeps track of change
132            if not s then
133                s = { tag = tag, data = { } }
134                split[entry] = s
135            end
136            s.data[#s.data+1] = v
137        end
138        list.results = split
139        -- done
140        list.sorted = true
141    end
142end
143
144function lxmlsorters.flush(name,setup)
145    local list = lists[name]
146    local results = list and list.results
147    local xmlw = context.xmlw
148    if results and next(results) then
149        for key, result in next, results do
150            local tag, data = result.tag, result.data
151            for d=1,#data do
152                xmlw(setup,data[d].entry)
153            end
154        end
155    else
156        local entries = list and list.entries
157        if entries then
158            for i=1,#entries do
159                xmlw(setup,entries[i][1])
160            end
161        end
162    end
163end
164