diff options
author | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-05-20 00:18:15 +0000 |
---|---|---|
committer | Ian Lance Taylor <ian@gcc.gnu.org> | 2011-05-20 00:18:15 +0000 |
commit | 9ff56c9570642711d5b7ab29920ecf5dbff14a27 (patch) | |
tree | c891bdec1e6f073f73fedeef23718bc3ac30d499 /libgo/go/xml | |
parent | 37cb25ed7acdb844b218231130e54b8b7a0ff6e6 (diff) | |
download | gcc-9ff56c9570642711d5b7ab29920ecf5dbff14a27.zip gcc-9ff56c9570642711d5b7ab29920ecf5dbff14a27.tar.gz gcc-9ff56c9570642711d5b7ab29920ecf5dbff14a27.tar.bz2 |
Update to current version of Go library.
From-SVN: r173931
Diffstat (limited to 'libgo/go/xml')
-rw-r--r-- | libgo/go/xml/read.go | 119 | ||||
-rw-r--r-- | libgo/go/xml/read_test.go | 8 | ||||
-rw-r--r-- | libgo/go/xml/xml.go | 73 | ||||
-rw-r--r-- | libgo/go/xml/xml_test.go | 173 |
4 files changed, 259 insertions, 114 deletions
diff --git a/libgo/go/xml/read.go b/libgo/go/xml/read.go index 9ae3bb8..e2b349c 100644 --- a/libgo/go/xml/read.go +++ b/libgo/go/xml/read.go @@ -139,8 +139,8 @@ import ( // to a freshly allocated value and then mapping the element to that value. // func Unmarshal(r io.Reader, val interface{}) os.Error { - v, ok := reflect.NewValue(val).(*reflect.PtrValue) - if !ok { + v := reflect.ValueOf(val) + if v.Kind() != reflect.Ptr { return os.NewError("non-pointer passed to Unmarshal") } p := NewParser(r) @@ -176,8 +176,8 @@ func (e *TagPathError) String() string { // Passing a nil start element indicates that Unmarshal should // read the token stream to find the start element. func (p *Parser) Unmarshal(val interface{}, start *StartElement) os.Error { - v, ok := reflect.NewValue(val).(*reflect.PtrValue) - if !ok { + v := reflect.ValueOf(val) + if v.Kind() != reflect.Ptr { return os.NewError("non-pointer passed to Unmarshal") } return p.unmarshal(v.Elem(), start) @@ -219,14 +219,11 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { } } - if pv, ok := val.(*reflect.PtrValue); ok { - if pv.Get() == 0 { - zv := reflect.MakeZero(pv.Type().(*reflect.PtrType).Elem()) - pv.PointTo(zv) - val = zv - } else { - val = pv.Elem() + if pv := val; pv.Kind() == reflect.Ptr { + if pv.IsNil() { + pv.Set(reflect.New(pv.Type().Elem())) } + val = pv.Elem() } var ( @@ -237,17 +234,17 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { saveXML reflect.Value saveXMLIndex int saveXMLData []byte - sv *reflect.StructValue - styp *reflect.StructType + sv reflect.Value + styp reflect.Type fieldPaths map[string]pathInfo ) - switch v := val.(type) { + switch v := val; v.Kind() { default: return os.ErrorString("unknown type " + v.Type().String()) - case *reflect.SliceValue: - typ := v.Type().(*reflect.SliceType) + case reflect.Slice: + typ := v.Type() if typ.Elem().Kind() == reflect.Uint8 { // []byte saveData = v @@ -269,23 +266,23 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { v.SetLen(n + 1) // Recur to read element into slice. - if err := p.unmarshal(v.Elem(n), start); err != nil { + if err := p.unmarshal(v.Index(n), start); err != nil { v.SetLen(n) return err } return nil - case *reflect.BoolValue, *reflect.FloatValue, *reflect.IntValue, *reflect.UintValue, *reflect.StringValue: + case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.String: saveData = v - case *reflect.StructValue: + case reflect.Struct: if _, ok := v.Interface().(Name); ok { - v.Set(reflect.NewValue(start.Name).(*reflect.StructValue)) + v.Set(reflect.ValueOf(start.Name)) break } sv = v - typ := sv.Type().(*reflect.StructType) + typ := sv.Type() styp = typ // Assign name. if f, ok := typ.FieldByName("XMLName"); ok { @@ -316,7 +313,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { if _, ok := v.Interface().(Name); !ok { return UnmarshalError(sv.Type().String() + " field XMLName does not have type xml.Name") } - v.(*reflect.StructValue).Set(reflect.NewValue(start.Name).(*reflect.StructValue)) + v.Set(reflect.ValueOf(start.Name)) } // Assign attributes. @@ -325,8 +322,8 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { f := typ.Field(i) switch f.Tag { case "attr": - strv, ok := sv.FieldByIndex(f.Index).(*reflect.StringValue) - if !ok { + strv := sv.FieldByIndex(f.Index) + if strv.Kind() != reflect.String { return UnmarshalError(sv.Type().String() + " field " + f.Name + " has attr tag but is not type string") } // Look for attribute. @@ -338,20 +335,20 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { break } } - strv.Set(val) + strv.SetString(val) case "comment": - if saveComment == nil { + if !saveComment.IsValid() { saveComment = sv.FieldByIndex(f.Index) } case "chardata": - if saveData == nil { + if !saveData.IsValid() { saveData = sv.FieldByIndex(f.Index) } case "innerxml": - if saveXML == nil { + if !saveXML.IsValid() { saveXML = sv.FieldByIndex(f.Index) if p.saved == nil { saveXMLIndex = 0 @@ -387,7 +384,7 @@ func (p *Parser) unmarshal(val reflect.Value, start *StartElement) os.Error { Loop: for { var savedOffset int - if saveXML != nil { + if saveXML.IsValid() { savedOffset = p.savedOffset() } tok, err := p.Token() @@ -398,7 +395,7 @@ Loop: case StartElement: // Sub-element. // Look up by tag name. - if sv != nil { + if sv.IsValid() { k := fieldName(t.Name.Local) if fieldPaths != nil { @@ -437,7 +434,7 @@ Loop: } case EndElement: - if saveXML != nil { + if saveXML.IsValid() { saveXMLData = p.saved.Bytes()[saveXMLIndex:savedOffset] if saveXMLIndex == 0 { p.saved = nil @@ -446,12 +443,12 @@ Loop: break Loop case CharData: - if saveData != nil { + if saveData.IsValid() { data = append(data, t...) } case Comment: - if saveComment != nil { + if saveComment.IsValid() { comment = append(comment, t...) } } @@ -479,50 +476,50 @@ Loop: } // Save accumulated data and comments - switch t := saveData.(type) { - case nil: + switch t := saveData; t.Kind() { + case reflect.Invalid: // Probably a comment, handled below default: return os.ErrorString("cannot happen: unknown type " + t.Type().String()) - case *reflect.IntValue: + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if !getInt64() { return err } - t.Set(itmp) - case *reflect.UintValue: + t.SetInt(itmp) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: if !getUint64() { return err } - t.Set(utmp) - case *reflect.FloatValue: + t.SetUint(utmp) + case reflect.Float32, reflect.Float64: if !getFloat64() { return err } - t.Set(ftmp) - case *reflect.BoolValue: + t.SetFloat(ftmp) + case reflect.Bool: value, err := strconv.Atob(strings.TrimSpace(string(data))) if err != nil { return err } - t.Set(value) - case *reflect.StringValue: - t.Set(string(data)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(data).(*reflect.SliceValue)) + t.SetBool(value) + case reflect.String: + t.SetString(string(data)) + case reflect.Slice: + t.Set(reflect.ValueOf(data)) } - switch t := saveComment.(type) { - case *reflect.StringValue: - t.Set(string(comment)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(comment).(*reflect.SliceValue)) + switch t := saveComment; t.Kind() { + case reflect.String: + t.SetString(string(comment)) + case reflect.Slice: + t.Set(reflect.ValueOf(comment)) } - switch t := saveXML.(type) { - case *reflect.StringValue: - t.Set(string(saveXMLData)) - case *reflect.SliceValue: - t.Set(reflect.NewValue(saveXMLData).(*reflect.SliceValue)) + switch t := saveXML; t.Kind() { + case reflect.String: + t.SetString(string(saveXMLData)) + case reflect.Slice: + t.Set(reflect.ValueOf(saveXMLData)) } return nil @@ -537,7 +534,7 @@ type pathInfo struct { // paths map with all paths leading to it ("a", "a>b", and "a>b>c"). // It is okay for paths to share a common, shorter prefix but not ok // for one path to itself be a prefix of another. -func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path string, fieldIdx []int) os.Error { +func addFieldPath(sv reflect.Value, paths map[string]pathInfo, path string, fieldIdx []int) os.Error { if info, found := paths[path]; found { return tagError(sv, info.fieldIdx, fieldIdx) } @@ -560,8 +557,8 @@ func addFieldPath(sv *reflect.StructValue, paths map[string]pathInfo, path strin } -func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error { - t := sv.Type().(*reflect.StructType) +func tagError(sv reflect.Value, idx1 []int, idx2 []int) os.Error { + t := sv.Type() f1 := t.FieldByIndex(idx1) f2 := t.FieldByIndex(idx2) return &TagPathError{t, f1.Name, f1.Tag, f2.Name, f2.Tag} @@ -569,7 +566,7 @@ func tagError(sv *reflect.StructValue, idx1 []int, idx2 []int) os.Error { // unmarshalPaths walks down an XML structure looking for // wanted paths, and calls unmarshal on them. -func (p *Parser) unmarshalPaths(sv *reflect.StructValue, paths map[string]pathInfo, path string, start *StartElement) os.Error { +func (p *Parser) unmarshalPaths(sv reflect.Value, paths map[string]pathInfo, path string, start *StartElement) os.Error { if info, _ := paths[path]; info.complete { return p.unmarshal(sv.FieldByIndex(info.fieldIdx), start) } diff --git a/libgo/go/xml/read_test.go b/libgo/go/xml/read_test.go index a6b9a8e..d4ae370 100644 --- a/libgo/go/xml/read_test.go +++ b/libgo/go/xml/read_test.go @@ -288,9 +288,7 @@ var pathTests = []interface{}{ func TestUnmarshalPaths(t *testing.T) { for _, pt := range pathTests { - p := reflect.MakeZero(reflect.NewValue(pt).Type()).(*reflect.PtrValue) - p.PointTo(reflect.MakeZero(p.Type().(*reflect.PtrType).Elem())) - v := p.Interface() + v := reflect.New(reflect.TypeOf(pt).Elem()).Interface() if err := Unmarshal(StringReader(pathTestString), v); err != nil { t.Fatalf("Unmarshal: %s", err) } @@ -315,8 +313,8 @@ type BadPathTestB struct { var badPathTests = []struct { v, e interface{} }{ - {&BadPathTestA{}, &TagPathError{reflect.Typeof(BadPathTestA{}), "First", "items>item1", "Second", "items>"}}, - {&BadPathTestB{}, &TagPathError{reflect.Typeof(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}}, + {&BadPathTestA{}, &TagPathError{reflect.TypeOf(BadPathTestA{}), "First", "items>item1", "Second", "items>"}}, + {&BadPathTestB{}, &TagPathError{reflect.TypeOf(BadPathTestB{}), "First", "items>item1", "Second", "items>item1>value"}}, } func TestUnmarshalBadPaths(t *testing.T) { diff --git a/libgo/go/xml/xml.go b/libgo/go/xml/xml.go index f92abe8..42d8b98 100644 --- a/libgo/go/xml/xml.go +++ b/libgo/go/xml/xml.go @@ -163,6 +163,13 @@ type Parser struct { // "quot": `"`, Entity map[string]string + // CharsetReader, if non-nil, defines a function to generate + // charset-conversion readers, converting from the provided + // non-UTF-8 charset into UTF-8. If CharsetReader is nil or + // returns an error, parsing stops with an error. One of the + // the CharsetReader's result values must be non-nil. + CharsetReader func(charset string, input io.Reader) (io.Reader, os.Error) + r io.ByteReader buf bytes.Buffer saved *bytes.Buffer @@ -186,17 +193,7 @@ func NewParser(r io.Reader) *Parser { line: 1, Strict: true, } - - // Get efficient byte at a time reader. - // Assume that if reader has its own - // ReadByte, it's efficient enough. - // Otherwise, use bufio. - if rb, ok := r.(io.ByteReader); ok { - p.r = rb - } else { - p.r = bufio.NewReader(r) - } - + p.switchToReader(r) return p } @@ -290,6 +287,18 @@ func (p *Parser) translate(n *Name, isElementName bool) { } } +func (p *Parser) switchToReader(r io.Reader) { + // Get efficient byte at a time reader. + // Assume that if reader has its own + // ReadByte, it's efficient enough. + // Otherwise, use bufio. + if rb, ok := r.(io.ByteReader); ok { + p.r = rb + } else { + p.r = bufio.NewReader(r) + } +} + // Parsing state - stack holds old name space translations // and the current set of open elements. The translations to pop when // ending a given tag are *below* it on the stack, which is @@ -487,6 +496,25 @@ func (p *Parser) RawToken() (Token, os.Error) { } data := p.buf.Bytes() data = data[0 : len(data)-2] // chop ?> + + if target == "xml" { + enc := procInstEncoding(string(data)) + if enc != "" && enc != "utf-8" && enc != "UTF-8" { + if p.CharsetReader == nil { + p.err = fmt.Errorf("xml: encoding %q declared but Parser.CharsetReader is nil", enc) + return nil, p.err + } + newr, err := p.CharsetReader(enc, p.r.(io.Reader)) + if err != nil { + p.err = fmt.Errorf("xml: opening charset %q: %v", enc, err) + return nil, p.err + } + if newr == nil { + panic("CharsetReader returned a nil Reader for charset " + enc) + } + p.switchToReader(newr) + } + } return ProcInst{target, data}, nil case '!': @@ -1633,3 +1661,26 @@ func Escape(w io.Writer, s []byte) { } w.Write(s[last:]) } + +// procInstEncoding parses the `encoding="..."` or `encoding='...'` +// value out of the provided string, returning "" if not found. +func procInstEncoding(s string) string { + // TODO: this parsing is somewhat lame and not exact. + // It works for all actual cases, though. + idx := strings.Index(s, "encoding=") + if idx == -1 { + return "" + } + v := s[idx+len("encoding="):] + if v == "" { + return "" + } + if v[0] != '\'' && v[0] != '"' { + return "" + } + idx = strings.IndexRune(v[1:], int(v[0])) + if idx == -1 { + return "" + } + return v[1 : idx+1] +} diff --git a/libgo/go/xml/xml_test.go b/libgo/go/xml/xml_test.go index 887bc3d..4e51cd5 100644 --- a/libgo/go/xml/xml_test.go +++ b/libgo/go/xml/xml_test.go @@ -9,6 +9,7 @@ import ( "io" "os" "reflect" + "strings" "testing" ) @@ -96,6 +97,19 @@ var cookedTokens = []Token{ Comment([]byte(" missing final newline ")), } +const testInputAltEncoding = ` +<?xml version="1.0" encoding="x-testing-uppercase"?> +<TAG>VALUE</TAG>` + +var rawTokensAltEncoding = []Token{ + CharData([]byte("\n")), + ProcInst{"xml", []byte(`version="1.0" encoding="x-testing-uppercase"`)}, + CharData([]byte("\n")), + StartElement{Name{"", "tag"}, nil}, + CharData([]byte("value")), + EndElement{Name{"", "tag"}}, +} + var xmlInput = []string{ // unexpected EOF cases "<", @@ -173,7 +187,64 @@ func StringReader(s string) io.Reader { return &stringReader{s, 0} } func TestRawToken(t *testing.T) { p := NewParser(StringReader(testInput)) + testRawToken(t, p, rawTokens) +} + +type downCaser struct { + t *testing.T + r io.ByteReader +} +func (d *downCaser) ReadByte() (c byte, err os.Error) { + c, err = d.r.ReadByte() + if c >= 'A' && c <= 'Z' { + c += 'a' - 'A' + } + return +} + +func (d *downCaser) Read(p []byte) (int, os.Error) { + d.t.Fatalf("unexpected Read call on downCaser reader") + return 0, os.EINVAL +} + +func TestRawTokenAltEncoding(t *testing.T) { + sawEncoding := "" + p := NewParser(StringReader(testInputAltEncoding)) + p.CharsetReader = func(charset string, input io.Reader) (io.Reader, os.Error) { + sawEncoding = charset + if charset != "x-testing-uppercase" { + t.Fatalf("unexpected charset %q", charset) + } + return &downCaser{t, input.(io.ByteReader)}, nil + } + testRawToken(t, p, rawTokensAltEncoding) +} + +func TestRawTokenAltEncodingNoConverter(t *testing.T) { + p := NewParser(StringReader(testInputAltEncoding)) + token, err := p.RawToken() + if token == nil { + t.Fatalf("expected a token on first RawToken call") + } + if err != nil { + t.Fatal(err) + } + token, err = p.RawToken() + if token != nil { + t.Errorf("expected a nil token; got %#v", token) + } + if err == nil { + t.Fatalf("expected an error on second RawToken call") + } + const encoding = "x-testing-uppercase" + if !strings.Contains(err.String(), encoding) { + t.Errorf("expected error to contain %q; got error: %v", + encoding, err) + } +} + +func testRawToken(t *testing.T, p *Parser, rawTokens []Token) { for i, want := range rawTokens { have, err := p.RawToken() if err != nil { @@ -258,47 +329,51 @@ func TestSyntax(t *testing.T) { } type allScalars struct { - True1 bool - True2 bool - False1 bool - False2 bool - Int int - Int8 int8 - Int16 int16 - Int32 int32 - Int64 int64 - Uint int - Uint8 uint8 - Uint16 uint16 - Uint32 uint32 - Uint64 uint64 - Uintptr uintptr - Float32 float32 - Float64 float64 - String string + True1 bool + True2 bool + False1 bool + False2 bool + Int int + Int8 int8 + Int16 int16 + Int32 int32 + Int64 int64 + Uint int + Uint8 uint8 + Uint16 uint16 + Uint32 uint32 + Uint64 uint64 + Uintptr uintptr + Float32 float32 + Float64 float64 + String string + PtrString *string } var all = allScalars{ - True1: true, - True2: true, - False1: false, - False2: false, - Int: 1, - Int8: -2, - Int16: 3, - Int32: -4, - Int64: 5, - Uint: 6, - Uint8: 7, - Uint16: 8, - Uint32: 9, - Uint64: 10, - Uintptr: 11, - Float32: 13.0, - Float64: 14.0, - String: "15", + True1: true, + True2: true, + False1: false, + False2: false, + Int: 1, + Int8: -2, + Int16: 3, + Int32: -4, + Int64: 5, + Uint: 6, + Uint8: 7, + Uint16: 8, + Uint32: 9, + Uint64: 10, + Uintptr: 11, + Float32: 13.0, + Float64: 14.0, + String: "15", + PtrString: &sixteen, } +var sixteen = "16" + const testScalarsInput = `<allscalars> <true1>true</true1> <true2>1</true2> @@ -319,6 +394,7 @@ const testScalarsInput = `<allscalars> <float32>13.0</float32> <float64>14.0</float64> <string>15</string> + <ptrstring>16</ptrstring> </allscalars>` func TestAllScalars(t *testing.T) { @@ -330,7 +406,7 @@ func TestAllScalars(t *testing.T) { t.Fatal(err) } if !reflect.DeepEqual(a, all) { - t.Errorf("expected %+v got %+v", all, a) + t.Errorf("have %+v want %+v", a, all) } } @@ -483,3 +559,26 @@ func TestDisallowedCharacters(t *testing.T) { } } } + +type procInstEncodingTest struct { + expect, got string +} + +var procInstTests = []struct { + input, expect string +}{ + {`version="1.0" encoding="utf-8"`, "utf-8"}, + {`version="1.0" encoding='utf-8'`, "utf-8"}, + {`version="1.0" encoding='utf-8' `, "utf-8"}, + {`version="1.0" encoding=utf-8`, ""}, + {`encoding="FOO" `, "FOO"}, +} + +func TestProcInstEncoding(t *testing.T) { + for _, test := range procInstTests { + got := procInstEncoding(test.input) + if got != test.expect { + t.Errorf("procInstEncoding(%q) = %q; want %q", test.input, got, test.expect) + } + } +} |