/
propBase.go
185 lines (172 loc) · 4.85 KB
/
propBase.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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package metal
import (
"fmt"
M "github.com/ionous/sashimi/compiler/model"
"github.com/ionous/sashimi/meta"
"github.com/ionous/sashimi/util/ident"
)
// FIX? to be comparable: would need to stop returning & of propBase,
// and -- i suspect -- would need to change getValue and setValue into non-object methods
type propBase struct {
mdl *Metal
src ident.Id
prop *M.PropertyModel
// life's a little complicated.
// we have a generic property base ( propBase )
// an extension to panic on every get and set ( panicValue )
// and overrides to implement the specific text/num/etc methods ( textValue )
// the location of values for class and instances differs, so the class and instance pass themselves to their properties, and on to their values.
getValue func(*M.PropertyModel) GenericValue
setValue func(*M.PropertyModel, GenericValue) error
}
func (p *propBase) set(v GenericValue) error {
return p.setValue(p.prop, v)
}
// mainly for arrays, but arrays dont have instance data storage
// if we need them, we probably will want to write a decoder on the instance json data.
func (p *propBase) getGeneric() GenericValue {
return p.getValue(p.prop)
}
// we want to keep values as ident.Id;
// values from json come in as string.
func (p *propBase) getId() (ret ident.Id) {
v := p.getValue(p.prop)
if id, ok := v.(ident.Id); ok {
ret = id
} else {
ret = ident.Id(v.(string))
}
return
}
// we want to keep values as float64;
// values from json come in as float64.
func (p *propBase) getNum() (ret float64) {
v := p.getValue(p.prop)
if id, ok := v.(float64); ok {
ret = id
} else {
ret = float64(v.(float64))
}
return
}
// we want to keep values as string
func (p *propBase) getString() string {
v := p.getValue(p.prop)
return v.(string)
}
func (p *propBase) String() string {
return fmt.Sprintf("%s.%s", p.src, p.prop.Id)
}
func (p *propBase) GetId() ident.Id {
return p.prop.Id
}
func (p *propBase) GetName() string {
return p.prop.Name
}
func (p *propBase) GetType() meta.PropertyType {
err := "invalid"
switch p.prop.Type {
case M.NumProperty:
x := meta.NumProperty
if p.prop.IsMany {
x |= meta.ArrayProperty
}
return x
case M.TextProperty:
x := meta.TextProperty
if p.prop.IsMany {
x |= meta.ArrayProperty
}
return x
case M.EnumProperty:
return meta.StateProperty
case M.PointerProperty:
x := meta.ObjectProperty
if p.prop.IsMany {
x |= meta.ArrayProperty
}
return x
default:
err = "unknown"
}
panic(fmt.Sprintf("GetType(%s.%s) has %s property type %T", p.src, p.prop.Id, err, p.prop))
}
func (p *propBase) GetValue() (ret meta.Value) {
err := "invalid"
switch p.prop.Type {
case M.NumProperty:
if !p.prop.IsMany {
return &numValue{panicValue{p}}
}
case M.TextProperty:
if !p.prop.IsMany {
return &textValue{panicValue{p}}
}
case M.EnumProperty:
if !p.prop.IsMany {
return &enumValue{panicValue{p}}
}
case M.PointerProperty:
if !p.prop.IsMany {
// we are not many, since we dont have many to many
// the far side must be either one or many.
// many values are "views" onto this objet's own properties
// one values are real properties, and need to be set.
if rel, ok := p.mdl.Relations[p.prop.Relation]; ok && rel.Style == M.OneToOne {
return newRelatedValue(p, rel)
} else {
return &pointerValue{panicValue{p}}
}
}
default:
err = "unknown"
}
panic(fmt.Sprintf("GetValue(%s.%s) has %s property type %v", p.src, p.prop.Id, err, p.prop.Type))
}
func (p *propBase) GetValues() meta.Values {
err := "invalid"
switch p.prop.Type {
case M.NumProperty:
if p.prop.IsMany {
return arrayValues{p, func(i int) meta.Value {
return &numElement{&elementValue{panicValue{p}, i}}
}}
}
case M.TextProperty:
if p.prop.IsMany {
return arrayValues{p, func(i int) meta.Value {
return &textElement{&elementValue{panicValue{p}, i}}
}}
}
case M.EnumProperty:
//
case M.PointerProperty:
if p.prop.IsMany {
if !p.prop.Relation.Empty() {
return newManyValues(p)
} else {
return arrayValues{p, func(i int) meta.Value {
return &objectElement{&elementValue{panicValue{p}, i}}
}}
}
}
default:
err = "unknown"
}
panic(fmt.Sprintf("GetValues(%s.%s) has %s property type %T", p.src, p.prop.Id, err, p.prop))
}
// FIX: this exists for backwards compatiblity with the client.
// the reality is, a relation effects a table, there may be multiple views that need updating. either the client could do this by seeing the relation and pulling new data,
// or we could push all of thep. this pushes just one. ( client pulling might be best )
func (p *propBase) GetRelative() (ret meta.Relative, okay bool) {
// get the relation
if relation, ok := p.mdl.Relations[p.prop.Relation]; ok {
// get the reverse property
okay, ret = true, meta.Relative{
Relation: p.prop.Relation,
Relates: p.prop.Relates,
From: relation.GetOther(p.prop.Id),
}
}
return
}