1
0
mirror of https://github.com/golang/go synced 2024-11-21 18:44:45 -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:
Rob Pike 2011-02-04 15:21:08 -08:00
parent 6e615a57af
commit 5008c63e6e
2 changed files with 75 additions and 5 deletions

View File

@ -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?

View File

@ -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"}