Files
cloudpods/vendor/github.com/libvirt/libvirt-go-xml/xmlutil.go
wanyaoqi f0810bbe88 guest import from libvirt (#11)
* import servers from libvirt

* dep add libvirt-go-xml

* fix code

* fix code
2019-04-01 23:07:41 +08:00

292 lines
7.7 KiB
Go

/*
* This file is part of the libvirt-go-xml project
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* Copyright (C) 2017 Red Hat, Inc.
*
*/
package libvirtxml
import (
"encoding/xml"
"fmt"
"strconv"
"strings"
)
type element struct {
XMLNS string
Name string
Attrs map[string]string
Content string
Children []*element
}
type elementstack []*element
func (s *elementstack) push(v *element) {
*s = append(*s, v)
}
func (s *elementstack) pop() *element {
res := (*s)[len(*s)-1]
*s = (*s)[:len(*s)-1]
return res
}
func getNamespaceURI(xmlnsMap map[string]string, xmlns string, name xml.Name) string {
if name.Space != "" {
uri, ok := xmlnsMap[name.Space]
if !ok {
return "undefined://" + name.Space
} else {
return uri
}
} else {
return xmlns
}
}
func xmlName(xmlns string, name xml.Name) string {
if xmlns == "" {
return name.Local
}
return name.Local + "(" + xmlns + ")"
}
func loadXML(xmlstr string, ignoreNSDecl bool) (*element, error) {
xmlnsMap := make(map[string]string)
xmlr := strings.NewReader(xmlstr)
d := xml.NewDecoder(xmlr)
var root *element
stack := elementstack{}
for {
t, err := d.RawToken()
if err != nil {
return nil, err
}
var parent *element
if root != nil {
if len(stack) == 0 {
return nil, fmt.Errorf("Unexpectedly empty stack")
}
parent = stack[len(stack)-1]
}
switch t := t.(type) {
case xml.StartElement:
xmlns := ""
if parent != nil {
xmlns = parent.XMLNS
}
for _, a := range t.Attr {
if a.Name.Space == "xmlns" {
xmlnsMap[a.Name.Local] = a.Value
} else if a.Name.Space == "" && a.Name.Local == "xmlns" {
xmlns = a.Value
}
}
xmlns = getNamespaceURI(xmlnsMap, xmlns, t.Name)
child := &element{
XMLNS: xmlns,
Name: xmlName(xmlns, t.Name),
Attrs: make(map[string]string),
}
for _, a := range t.Attr {
if a.Name.Space == "xmlns" {
continue
}
if a.Name.Space == "" && a.Name.Local == "xmlns" {
continue
}
attrNS := getNamespaceURI(xmlnsMap, "", a.Name)
child.Attrs[xmlName(attrNS, a.Name)] = a.Value
}
stack.push(child)
if root == nil {
root = child
} else {
parent.Children = append(parent.Children, child)
parent.Content = ""
}
case xml.EndElement:
stack.pop()
case xml.CharData:
if parent != nil && len(parent.Children) == 0 {
val := string(t)
if strings.TrimSpace(val) != "" {
parent.Content = val
}
}
}
if root != nil && len(stack) == 0 {
break
}
}
return root, nil
}
func testCompareValue(filename, path, key, expected, actual string) error {
if expected == actual {
return nil
}
i1, err1 := strconv.ParseInt(expected, 0, 64)
i2, err2 := strconv.ParseInt(actual, 0, 64)
if err1 == nil && err2 == nil && i1 == i2 {
return nil
}
path = path + "/@" + key
return fmt.Errorf("%s: %s: attribute actual value '%s' does not match expected value '%s'",
filename, path, actual, expected)
}
func testCompareElement(filename, expectPath, actualPath string, expect, actual *element, extraExpectNodes, extraActualNodes map[string]bool) error {
if expect.Name != actual.Name {
return fmt.Errorf("%s: name '%s' doesn't match '%s'",
expectPath, expect.Name, actual.Name)
}
expectAttr := expect.Attrs
for key, val := range actual.Attrs {
expectval, ok := expectAttr[key]
if !ok {
attrPath := actualPath + "/@" + key
if _, ok := extraActualNodes[attrPath]; ok {
continue
}
return fmt.Errorf("%s: %s: attribute in actual XML missing in expected XML",
filename, attrPath)
}
err := testCompareValue(filename, actualPath, key, expectval, val)
if err != nil {
return err
}
delete(expectAttr, key)
}
for key, _ := range expectAttr {
attrPath := expectPath + "/@" + key
if _, ok := extraExpectNodes[attrPath]; ok {
continue
}
return fmt.Errorf("%s: %s: attribute '%s' in expected XML missing in actual XML",
filename, attrPath, expectAttr[key])
}
if expect.Content != actual.Content {
return fmt.Errorf("%s: %s: actual content '%s' does not match expected '%s'",
filename, actualPath, actual.Content, expect.Content)
}
used := make([]bool, len(actual.Children))
expectChildIndexes := make(map[string]uint)
actualChildIndexes := make(map[string]uint)
for _, expectChild := range expect.Children {
expectIndex, _ := expectChildIndexes[expectChild.Name]
expectChildIndexes[expectChild.Name] = expectIndex + 1
subExpectPath := fmt.Sprintf("%s/%s[%d]", expectPath, expectChild.Name, expectIndex)
var actualChild *element = nil
for i := 0; i < len(used); i++ {
if !used[i] && actual.Children[i].Name == expectChild.Name {
actualChild = actual.Children[i]
used[i] = true
break
}
}
if actualChild == nil {
if _, ok := extraExpectNodes[subExpectPath]; ok {
continue
}
return fmt.Errorf("%s: %s: element in expected XML missing in actual XML",
filename, subExpectPath)
}
actualIndex, _ := actualChildIndexes[actualChild.Name]
actualChildIndexes[actualChild.Name] = actualIndex + 1
subActualPath := fmt.Sprintf("%s/%s[%d]", actualPath, actualChild.Name, actualIndex)
err := testCompareElement(filename, subExpectPath, subActualPath, expectChild, actualChild, extraExpectNodes, extraActualNodes)
if err != nil {
return err
}
}
actualChildIndexes = make(map[string]uint)
for i, actualChild := range actual.Children {
actualIndex, _ := actualChildIndexes[actualChild.Name]
actualChildIndexes[actualChild.Name] = actualIndex + 1
if used[i] {
continue
}
subActualPath := fmt.Sprintf("%s/%s[%d]", actualPath, actualChild.Name, actualIndex)
if _, ok := extraActualNodes[subActualPath]; ok {
continue
}
return fmt.Errorf("%s: %s: element in actual XML missing in expected XML",
filename, subActualPath)
}
return nil
}
func makeExtraNodeMap(nodes []string) map[string]bool {
ret := make(map[string]bool)
for _, node := range nodes {
ret[node] = true
}
return ret
}
func testCompareXML(filename, expectStr, actualStr string, extraExpectNodes, extraActualNodes []string) error {
extraExpectNodeMap := makeExtraNodeMap(extraExpectNodes)
extraActualNodeMap := makeExtraNodeMap(extraActualNodes)
//fmt.Printf("%s\n", expectedstr)
expectRoot, err := loadXML(expectStr, true)
if err != nil {
return err
}
//fmt.Printf("%s\n", actualstr)
actualRoot, err := loadXML(actualStr, true)
if err != nil {
return err
}
if expectRoot.Name != actualRoot.Name {
return fmt.Errorf("%s: /: expected root element '%s' does not match actual '%s'",
filename, expectRoot.Name, actualRoot.Name)
}
err = testCompareElement(filename, "/"+expectRoot.Name+"[0]", "/"+actualRoot.Name+"[0]", expectRoot, actualRoot, extraExpectNodeMap, extraActualNodeMap)
if err != nil {
return err
}
return nil
}