/
location.go
162 lines (143 loc) · 4.58 KB
/
location.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
157
158
159
160
161
162
package index
import (
"context"
"fmt"
"os"
"strconv"
"time"
"perkeep.org/pkg/blob"
"perkeep.org/pkg/schema/nodeattr"
"perkeep.org/pkg/types/camtypes"
)
// LocationHelper queries permanode locations.
//
// A LocationHelper is not safe for concurrent use.
// Callers should use Lock or RLock on the underlying index instead.
type LocationHelper struct {
index *Index
corpus *Corpus // may be nil
}
// NewLocationHelper returns a new location handler
// that uses ix to query blob attributes.
func NewLocationHelper(ix *Index) *LocationHelper {
lh := &LocationHelper{index: ix}
if ix.corpus != nil {
lh.corpus = ix.corpus
}
return lh
}
// SetCorpus sets the corpus to be used
// for location lookups.
func (lh *LocationHelper) SetCorpus(corpus *Corpus) {
lh.corpus = corpus
}
// altLocationRef maps camliNodeType to a slice of attributes
// whose values may refer to permanodes with location information.
var altLocationRef = map[string][]string{
// TODO(mpl): twitter.
"foursquare.com:checkin": {"foursquareVenuePermanode"},
}
// PermanodeLocation returns the location info for a permanode,
// from one of the following sources:
// 1. Permanode attributes "latitude" and "longitude"
// 2. Referenced permanode attributes (eg. for "foursquare.com:checkin"
// its "foursquareVenuePermanode")
// 3. Location in permanode camliContent file metadata
//
// The sources are checked in this order, the location from
// the first source yielding a valid result is returned.
func (lh *LocationHelper) PermanodeLocation(ctx context.Context, permaNode blob.Ref,
at time.Time, owner *Owner) (camtypes.Location, error) {
return lh.permanodeLocation(ctx, permaNode, at, owner, true)
}
func (lh *LocationHelper) permanodeLocation(ctx context.Context,
pn blob.Ref, at time.Time, owner *Owner,
useRef bool) (loc camtypes.Location, err error) {
signerID := owner.KeyID() // might be empty
pa := permAttr{at: at, signerFilter: owner.RefSet(signerID)}
if lh.corpus != nil {
var claims []*camtypes.Claim
pa.attrs, claims = lh.corpus.permanodeAttrsOrClaims(pn, at, signerID)
if claims != nil {
pa.claims = claimPtrSlice(claims)
}
} else {
var claims []camtypes.Claim
claims, err = lh.index.AppendClaims(ctx, nil, pn, signerID, "")
if err != nil {
return camtypes.Location{}, err
}
pa.claims = claimSlice(claims)
}
// Rule 1: if permanode has an explicit latitude and longitude,
// then this is its location.
slat, slong := pa.get(nodeattr.Latitude), pa.get(nodeattr.Longitude)
if slat != "" && slong != "" {
lat, latErr := strconv.ParseFloat(slat, 64)
long, longErr := strconv.ParseFloat(slong, 64)
switch {
case latErr != nil:
err = fmt.Errorf("invalid latitude in %v: %v", pn, latErr)
case longErr != nil:
err = fmt.Errorf("invalid longitude in %v: %v", pn, longErr)
default:
err = nil
}
return camtypes.Location{Latitude: lat, Longitude: long}, err
}
if useRef {
// Rule 2: referenced permanode attributes
nodeType := pa.get(nodeattr.Type)
if nodeType != "" {
for _, a := range altLocationRef[nodeType] {
refPn, hasRef := blob.Parse(pa.get(a))
if !hasRef {
continue
}
loc, err = lh.permanodeLocation(ctx, refPn, at, owner, false)
if err == nil {
return loc, err
}
}
}
// Rule 3: location in permanode camliContent file metadata.
// Use this only if pn was the argument passed to sh.getPermanodeLocation,
// and is not something found through a reference via altLocationRef.
if content, ok := blob.Parse(pa.get(nodeattr.CamliContent)); ok {
return lh.index.GetFileLocation(ctx, content)
}
}
return camtypes.Location{}, os.ErrNotExist
}
// permAttr represents attributes of a permanode
// for a given owner at a given time, either
// from a Corpus or Index/Interface.
type permAttr struct {
// permanode attributes from Corpus.PermanodeAttrs
// This may be nil if corpus has no cache for the permanode
// for the given time, in that case claims must be used.
attrs map[string][]string
// claims of permanode
// Populated only when attrs is not valid; using AppendClaims
// of corpus, or of the index in the absence of a corpus.
// Both attrs and claims may be nil if the permanode
// does not exist, or all attributes of it are from
// signer(s) other than the signerFilter.
claims claimsIntf
at time.Time
signerFilter SignerRefSet
}
// get returns the value of attr.
func (pa permAttr) get(attr string) string {
if pa.attrs != nil {
v := pa.attrs[attr]
if len(v) != 0 {
return v[0]
}
return ""
}
if pa.claims != nil {
return claimsIntfAttrValue(pa.claims, attr, pa.at, pa.signerFilter)
}
return ""
}