mirror of
https://github.com/golang/go
synced 2024-11-25 02:57:57 -07:00
template: allow a leading '*' to indicate that evaulation should
indirect through a pointer. Fixes #1478. R=rsc, r2 CC=golang-dev https://golang.org/cl/4131045
This commit is contained in:
parent
6e615a57af
commit
5008c63e6e
@ -53,6 +53,13 @@
|
||||
If it is not found, the search continues in outer sections
|
||||
until the top level is reached.
|
||||
|
||||
If the field value is a pointer, leading asterisks indicate
|
||||
that the value to be inserted should be evaluated through the
|
||||
pointer. For example, if x.p is of type *int, {x.p} will
|
||||
insert the value of the pointer but {*x.p} will insert the
|
||||
value of the underlying integer. If the value is nil or not a
|
||||
pointer, asterisks have no effect.
|
||||
|
||||
If a formatter is specified, it must be named in the formatter
|
||||
map passed to the template set up routines or in the default
|
||||
set ("html","str","") and is used to process the data for
|
||||
@ -633,6 +640,23 @@ func (t *Template) lookup(st *state, v reflect.Value, name string) reflect.Value
|
||||
return v
|
||||
}
|
||||
|
||||
// indirectPtr returns the item numLevels levels of indirection below the value.
|
||||
// It is forgiving: if the value is not a pointer, it returns it rather than giving
|
||||
// an error. If the pointer is nil, it is returned as is.
|
||||
func indirectPtr(v reflect.Value, numLevels int) reflect.Value {
|
||||
for i := numLevels; v != nil && i > 0; i++ {
|
||||
if p, ok := v.(*reflect.PtrValue); ok {
|
||||
if p.IsNil() {
|
||||
return v
|
||||
}
|
||||
v = p.Elem()
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Walk v through pointers and interfaces, extracting the elements within.
|
||||
func indirect(v reflect.Value) reflect.Value {
|
||||
loop:
|
||||
@ -654,12 +678,16 @@ loop:
|
||||
// The special name "@" (the "cursor") denotes the current data.
|
||||
// The value coming in (st.data) might need indirecting to reach
|
||||
// a struct while the return value is not indirected - that is,
|
||||
// it represents the actual named field.
|
||||
// it represents the actual named field. Leading stars indicate
|
||||
// levels of indirection to be applied to the value.
|
||||
func (t *Template) findVar(st *state, s string) reflect.Value {
|
||||
if s == "@" {
|
||||
return st.data
|
||||
}
|
||||
data := st.data
|
||||
flattenedName := strings.TrimLeft(s, "*")
|
||||
numStars := len(s) - len(flattenedName)
|
||||
s = flattenedName
|
||||
if s == "@" {
|
||||
return indirectPtr(data, numStars)
|
||||
}
|
||||
for _, elem := range strings.Split(s, ".", -1) {
|
||||
// Look up field; data must be a struct or map.
|
||||
data = t.lookup(st, data, elem)
|
||||
@ -667,7 +695,7 @@ func (t *Template) findVar(st *state, s string) reflect.Value {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return data
|
||||
return indirectPtr(data, numStars)
|
||||
}
|
||||
|
||||
// Is there no data to look at?
|
||||
|
@ -31,7 +31,10 @@ type U struct {
|
||||
|
||||
type S struct {
|
||||
Header string
|
||||
HeaderPtr *string
|
||||
Integer int
|
||||
IntegerPtr *int
|
||||
NilPtr *int
|
||||
Raw string
|
||||
InnerT T
|
||||
InnerPointerT *T
|
||||
@ -47,6 +50,7 @@ type S struct {
|
||||
JSON interface{}
|
||||
Innermap U
|
||||
Stringmap map[string]string
|
||||
Ptrmap map[string]*string
|
||||
Bytes []byte
|
||||
Iface interface{}
|
||||
Ifaceptr interface{}
|
||||
@ -118,6 +122,24 @@ var tests = []*Test{
|
||||
out: "Header=77\n",
|
||||
},
|
||||
|
||||
&Test{
|
||||
in: "Pointers: {*HeaderPtr}={*IntegerPtr}\n",
|
||||
|
||||
out: "Pointers: Header=77\n",
|
||||
},
|
||||
|
||||
&Test{
|
||||
in: "Stars but not pointers: {*Header}={*Integer}\n",
|
||||
|
||||
out: "Stars but not pointers: Header=77\n",
|
||||
},
|
||||
|
||||
&Test{
|
||||
in: "nil pointer: {*NilPtr}={*Integer}\n",
|
||||
|
||||
out: "nil pointer: <nil>=77\n",
|
||||
},
|
||||
|
||||
// Method at top level
|
||||
&Test{
|
||||
in: "ptrmethod={PointerMethod}\n",
|
||||
@ -407,6 +429,20 @@ var tests = []*Test{
|
||||
out: "\tstringresult\n" +
|
||||
"\tstringresult\n",
|
||||
},
|
||||
&Test{
|
||||
in: "{*Ptrmap.stringkey1}\n",
|
||||
|
||||
out: "pointedToString\n",
|
||||
},
|
||||
&Test{
|
||||
in: "{.repeated section Ptrmap}\n" +
|
||||
"{*@}\n" +
|
||||
"{.end}",
|
||||
|
||||
out: "pointedToString\n" +
|
||||
"pointedToString\n",
|
||||
},
|
||||
|
||||
|
||||
// Interface values
|
||||
|
||||
@ -460,7 +496,9 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
|
||||
s := new(S)
|
||||
// initialized by hand for clarity.
|
||||
s.Header = "Header"
|
||||
s.HeaderPtr = &s.Header
|
||||
s.Integer = 77
|
||||
s.IntegerPtr = &s.Integer
|
||||
s.Raw = "&<>!@ #$%^"
|
||||
s.InnerT = t1
|
||||
s.Data = []T{t1, t2}
|
||||
@ -480,6 +518,10 @@ func testAll(t *testing.T, parseFunc func(*Test) (*Template, os.Error)) {
|
||||
s.Stringmap = make(map[string]string)
|
||||
s.Stringmap["stringkey1"] = "stringresult" // the same value so repeated section is order-independent
|
||||
s.Stringmap["stringkey2"] = "stringresult"
|
||||
s.Ptrmap = make(map[string]*string)
|
||||
x := "pointedToString"
|
||||
s.Ptrmap["stringkey1"] = &x // the same value so repeated section is order-independent
|
||||
s.Ptrmap["stringkey2"] = &x
|
||||
s.Bytes = []byte("hello")
|
||||
s.Iface = []int{1, 2, 3}
|
||||
s.Ifaceptr = &T{"Item", "Value"}
|
||||
|
Loading…
Reference in New Issue
Block a user