forked from paulmach/osm
-
Notifications
You must be signed in to change notification settings - Fork 2
/
scanner.go
156 lines (134 loc) · 3.18 KB
/
scanner.go
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
151
152
153
154
155
156
package osmxml
import (
"context"
"encoding/xml"
"io"
"strings"
"github.com/onXmaps/osm"
)
var _ osm.Scanner = &Scanner{}
// Scanner provides a convenient interface reading a stream of osm data
// from a file or url. Successive calls to the Scan method will step through the data.
//
// Scanning stops unrecoverably at EOF, the first I/O error, the first xml error or
// the context being cancelled. When a scan stops, the reader may have advanced
// arbitrarily far past the last token.
//
// The Scanner API is based on bufio.Scanner
// https://golang.org/pkg/bufio/#Scanner
type Scanner struct {
ctx context.Context
done context.CancelFunc
closed bool
decoder *xml.Decoder
next osm.Object
err error
}
// New returns a new Scanner to read from r.
func New(ctx context.Context, r io.Reader) *Scanner {
if ctx == nil {
ctx = context.Background()
}
s := &Scanner{
decoder: xml.NewDecoder(r),
}
s.ctx, s.done = context.WithCancel(ctx)
return s
}
// Close causes all future calls to Scan to return false.
// Does not close the underlying reader.
func (s *Scanner) Close() error {
s.closed = true
s.done()
return nil
}
// Scan advances the Scanner to the next element, which will then be available
// through the Object method. It returns false when the scan stops, either
// by reaching the end of the input, an io error, an xml error or the context
// being cancelled. After Scan returns false, the Err method will return any
// error that occurred during scanning, except if it was io.EOF, Err will
// return nil.
func (s *Scanner) Scan() bool {
if s.err != nil {
return false
}
Loop:
for {
if s.ctx.Err() != nil {
return false
}
t, err := s.decoder.Token()
if err != nil {
s.err = err
return false
}
se, ok := t.(xml.StartElement)
if !ok {
continue
}
s.next = nil
switch strings.ToLower(se.Name.Local) {
case "bounds":
bounds := &osm.Bounds{}
err = s.decoder.DecodeElement(&bounds, &se)
s.next = bounds
case "node":
node := &osm.Node{}
err = s.decoder.DecodeElement(&node, &se)
s.next = node
case "way":
way := &osm.Way{}
err = s.decoder.DecodeElement(&way, &se)
s.next = way
case "relation":
relation := &osm.Relation{}
err = s.decoder.DecodeElement(&relation, &se)
s.next = relation
case "changeset":
cs := &osm.Changeset{}
err = s.decoder.DecodeElement(&cs, &se)
s.next = cs
case "note":
n := &osm.Note{}
err = s.decoder.DecodeElement(&n, &se)
s.next = n
case "user":
u := &osm.User{}
err = s.decoder.DecodeElement(&u, &se)
s.next = u
default:
continue Loop
}
if err != nil {
s.err = err
return false
}
return true
}
}
// Object returns the most recent token generated by a call to Scan
// as a new osm.Object. This interface is implemented by:
//
// *osm.Bounds
// *osm.Node
// *osm.Way
// *osm.Relation
// *osm.Changeset
// *osm.Note
// *osm.User
func (s *Scanner) Object() osm.Object {
return s.next
}
// Err returns the first non-EOF error that was encountered by the Scanner.
func (s *Scanner) Err() error {
if s.err == io.EOF {
return nil
}
if s.err != nil {
return s.err
}
if s.closed {
return osm.ErrScannerClosed
}
return s.ctx.Err()
}