// EncodeToken allows writing a [ProcInst] with Target set to "xml" only as the first token
// in the stream.
func (enc *Encoder) EncodeToken(t Token) error {
-
p := &enc.p
switch t := t.(type) {
case StartElement:
p.WriteString("<!--")
p.Write(t)
p.WriteString("-->")
- return p.cachedWriteError()
case ProcInst:
// First token to be encoded which is also a ProcInst with target of xml
// is the xml declaration. The only ProcInst where target of xml is allowed.
- if t.Target == "xml" && p.w.Buffered() != 0 {
+ if t.Target == "xml" && p.wroteNonWS {
return fmt.Errorf("xml: EncodeToken of ProcInst xml target only valid for xml declaration, first token encoded")
}
if !isNameString(t.Target) {
p.WriteString(">")
default:
return fmt.Errorf("xml: EncodeToken of invalid token type")
+ }
+ if err := p.cachedWriteError(); err != nil || enc.p.wroteNonWS {
+ return err
+ }
+ enc.p.wroteNonWS = !isWhitespace(t)
+ return nil
+}
+// isWhitespace reports whether t is a CharData token consisting entirely of
+// XML whitespace.
+func isWhitespace(t Token) bool {
+ switch t := t.(type) {
+ case CharData:
+ for _, b := range t {
+ switch b {
+ case ' ', '\r', '\n', '\t':
+ default:
+ return false
+ }
+ }
+ return true
+ default:
+ return false
}
- return p.cachedWriteError()
}
// isValidDirective reports whether dir is a valid directive text,
prefixes []string
tags []Name
closed bool
+ wroteNonWS bool
err error
}
var buf bytes.Buffer
enc := NewEncoder(&buf)
+ if err := enc.EncodeToken(CharData(" \n\r\t")); err != nil {
+ t.Fatal("enc.EncodeToken: expected to be able to encode whitespace as first token")
+ }
if err := enc.EncodeToken(ProcInst{"xml", []byte("Instruction")}); err != nil {
t.Fatalf("enc.EncodeToken: expected to be able to encode xml target ProcInst as first token, %s", err)
}
line int
linestart int64
offset int64
+ readNonWS bool
unmarshalDepth int
}
return EndElement{d.toClose}, nil
}
+ readNonWS := d.readNonWS
+
b, ok := d.getc()
if !ok {
return nil, d.err
if data == nil {
return nil, d.err
}
+ if !d.readNonWS && !isWhitespace(CharData(data)) {
+ d.readNonWS = true
+ }
return CharData(data), nil
}
+ d.readNonWS = true
if b, ok = d.mustgetc(); !ok {
return nil, d.err
data = data[0 : len(data)-2] // chop ?>
if target == "xml" {
+ if readNonWS {
+ d.err = errors.New("xml: XML declaration after start of document")
+ return nil, d.err
+ }
+
content := string(data)
ver := procInst("version", content)
if ver != "" && ver != "1.0" {
// Header-related errors.
{`<?xml version="1.1" encoding="UTF-8"?>`, `unsupported version "1.1"; only version 1.0 is supported`},
+ {`<foo><?xml version="1.0"?>`, `XML declaration after start of document`},
// Cases below are for "no errors".
{withDefaultHeader(`<?ok?>`), ``},
{withDefaultHeader(`<?ok version="ok"?>`), ``},
+ {` <?xml version="1.0"?>`, ``},
}
for _, test := range tests {