vendor: revendor

This commit is contained in:
Phu Kieu
2017-03-24 11:03:30 -07:00
parent b5f70dac36
commit 4b457d8c82
9 changed files with 538 additions and 264 deletions

View File

@@ -0,0 +1,98 @@
package etreeutils
import (
"sort"
"strings"
"github.com/beevik/etree"
)
// TransformExcC14n transforms the passed element into xml-exc-c14n form.
func TransformExcC14n(el *etree.Element, inclusiveNamespacesPrefixList string) error {
prefixes := strings.Fields(inclusiveNamespacesPrefixList)
prefixSet := make(map[string]struct{}, len(prefixes))
for _, prefix := range prefixes {
prefixSet[prefix] = struct{}{}
}
err := transformExcC14n(DefaultNSContext, EmptyNSContext, el, prefixSet)
if err != nil {
return err
}
return nil
}
func transformExcC14n(ctx, declared NSContext, el *etree.Element, inclusiveNamespaces map[string]struct{}) error {
scope, err := ctx.SubContext(el)
if err != nil {
return err
}
visiblyUtilizedPrefixes := map[string]struct{}{
el.Space: struct{}{},
}
filteredAttrs := []etree.Attr{}
// Filter out all namespace declarations
for _, attr := range el.Attr {
switch {
case attr.Space == xmlnsPrefix:
if _, ok := inclusiveNamespaces[attr.Key]; ok {
visiblyUtilizedPrefixes[attr.Key] = struct{}{}
}
case attr.Space == defaultPrefix && attr.Key == xmlnsPrefix:
if _, ok := inclusiveNamespaces[defaultPrefix]; ok {
visiblyUtilizedPrefixes[defaultPrefix] = struct{}{}
}
default:
if attr.Space != defaultPrefix {
visiblyUtilizedPrefixes[attr.Space] = struct{}{}
}
filteredAttrs = append(filteredAttrs, attr)
}
}
el.Attr = filteredAttrs
declared = declared.Copy()
// Declare all visibly utilized prefixes that are in-scope but haven't
// been declared in the canonicalized form yet. These might have been
// declared on this element but then filtered out above, or they might
// have been declared on an ancestor (before canonicalization) which
// didn't visibly utilize and thus had them removed.
for prefix := range visiblyUtilizedPrefixes {
// Skip redundant declarations - they have to already have the same
// value.
if declaredNamespace, ok := declared.prefixes[prefix]; ok {
if value, ok := scope.prefixes[prefix]; ok && declaredNamespace == value {
continue
}
}
namespace, err := scope.LookupPrefix(prefix)
if err != nil {
return err
}
el.Attr = append(el.Attr, declared.declare(prefix, namespace))
}
sort.Sort(SortedAttrs(el.Attr))
// Transform child elements
for _, child := range el.ChildElements() {
err := transformExcC14n(scope, declared, child, inclusiveNamespaces)
if err != nil {
return err
}
}
return nil
}

View File

@@ -47,13 +47,38 @@ type NSContext struct {
prefixes map[string]string
}
func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
// The subcontext should inherit existing declared prefixes
func (ctx NSContext) Copy() NSContext {
prefixes := make(map[string]string, len(ctx.prefixes)+4)
for k, v := range ctx.prefixes {
prefixes[k] = v
}
return NSContext{prefixes: prefixes}
}
func (ctx NSContext) declare(prefix, namespace string) etree.Attr {
ctx.prefixes[prefix] = namespace
switch prefix {
case defaultPrefix:
return etree.Attr{
Key: xmlnsPrefix,
Value: namespace,
}
default:
return etree.Attr{
Space: xmlnsPrefix,
Key: prefix,
Value: namespace,
}
}
}
func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
// The subcontext should inherit existing declared prefixes
newCtx := ctx.Copy()
// Merge new namespace declarations on top of existing ones.
for _, attr := range el.Attr {
if attr.Space == xmlnsPrefix {
@@ -69,7 +94,7 @@ func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
return ctx, ErrReservedNamespace
}
prefixes[attr.Key] = attr.Value
newCtx.declare(attr.Key, attr.Value)
} else if attr.Space == defaultPrefix && attr.Key == xmlnsPrefix {
// This attribute is a default namespace declaration
@@ -78,11 +103,21 @@ func (ctx NSContext) SubContext(el *etree.Element) (NSContext, error) {
return ctx, ErrInvalidDefaultNamespace
}
prefixes[defaultPrefix] = attr.Value
newCtx.declare(defaultPrefix, attr.Value)
}
}
return NSContext{prefixes: prefixes}, nil
return newCtx, nil
}
// Prefixes returns a copy of this context's prefix map.
func (ctx NSContext) Prefixes() map[string]string {
prefixes := make(map[string]string, len(ctx.prefixes))
for k, v := range ctx.prefixes {
prefixes[k] = v
}
return prefixes
}
// LookupPrefix attempts to find a declared namespace for the specified prefix. If the prefix
@@ -98,7 +133,13 @@ func (ctx NSContext) LookupPrefix(prefix string) (string, error) {
}
}
func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.Element) error) error {
// NSIterHandler is a function which is invoked with a element and its surrounding
// NSContext during traversals.
type NSIterHandler func(NSContext, *etree.Element) error
// NSTraverse traverses an element tree, invoking the passed handler for each element
// in the tree.
func NSTraverse(ctx NSContext, el *etree.Element, handle NSIterHandler) error {
ctx, err := ctx.SubContext(el)
if err != nil {
return err
@@ -111,7 +152,7 @@ func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.
// Recursively traverse child elements.
for _, child := range el.ChildElements() {
err := nsTraverse(ctx, child, handle)
err := NSTraverse(ctx, child, handle)
if err != nil {
return err
}
@@ -120,7 +161,9 @@ func nsTraverse(ctx NSContext, el *etree.Element, handle func(NSContext, *etree.
return nil
}
func detachWithNamespaces(ctx NSContext, el *etree.Element) (*etree.Element, error) {
// NSDetatch makes a copy of the passed element, and declares any namespaces in
// the passed context onto the new element before returning it.
func NSDetatch(ctx NSContext, el *etree.Element) (*etree.Element, error) {
ctx, err := ctx.SubContext(el)
if err != nil {
return nil, err
@@ -177,43 +220,52 @@ func detachWithNamespaces(ctx NSContext, el *etree.Element) (*etree.Element, err
return el, nil
}
// NSSelectOne conducts a depth-first search for an element with the specified namespace
// NSSelectOne behaves identically to NSSelectOneCtx, but uses DefaultNSContext as the
// surrounding context.
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
return NSSelectOneCtx(DefaultNSContext, el, namespace, tag)
}
// NSSelectOneCtx conducts a depth-first search for an element with the specified namespace
// and tag. If such an element is found, a new *etree.Element is returned which is a
// copy of the found element, but with all in-context namespace declarations attached
// to the element as attributes.
func NSSelectOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
func NSSelectOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
var found *etree.Element
err := nsTraverse(DefaultNSContext, el, func(ctx NSContext, el *etree.Element) error {
currentNS, err := ctx.LookupPrefix(el.Space)
err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
var err error
found, err = NSDetatch(ctx, el)
if err != nil {
return err
}
// Base case, el is the sought after element.
if currentNS == namespace && el.Tag == tag {
found, err = detachWithNamespaces(ctx, el)
return ErrTraversalHalted
}
return nil
return ErrTraversalHalted
})
if err != nil && err != ErrTraversalHalted {
if err != nil {
return nil, err
}
return found, nil
}
// NSFindIterate conducts a depth-first traversal searching for elements with the
// specified tag in the specified namespace. For each such element, the passed
// handler function is invoked. If the handler function returns an error
// traversal is immediately halted. If the error returned by the handler is
// ErrTraversalHalted then nil will be returned by NSFindIterate. If any other
// error is returned by the handler, that error will be returned by NSFindIterate.
func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.Element) error) error {
err := nsTraverse(DefaultNSContext, el, func(ctx NSContext, el *etree.Element) error {
// NSFindIterate behaves identically to NSFindIterateCtx, but uses DefaultNSContext
// as the surrounding context.
func NSFindIterate(el *etree.Element, namespace, tag string, handle NSIterHandler) error {
return NSFindIterateCtx(DefaultNSContext, el, namespace, tag, handle)
}
// NSFindIterateCtx conducts a depth-first traversal searching for elements with the
// specified tag in the specified namespace. It uses the passed NSContext for prefix
// lookups. For each such element, the passed handler function is invoked. If the
// handler function returns an error traversal is immediately halted. If the error
// returned by the handler is ErrTraversalHalted then nil will be returned by
// NSFindIterate. If any other error is returned by the handler, that error will be
// returned by NSFindIterate.
func NSFindIterateCtx(ctx NSContext, el *etree.Element, namespace, tag string, handle NSIterHandler) error {
err := NSTraverse(ctx, el, func(ctx NSContext, el *etree.Element) error {
currentNS, err := ctx.LookupPrefix(el.Space)
if err != nil {
return err
@@ -221,7 +273,7 @@ func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.
// Base case, el is the sought after element.
if currentNS == namespace && el.Tag == tag {
return handle(el)
return handle(ctx, el)
}
return nil
@@ -234,12 +286,18 @@ func NSFindIterate(el *etree.Element, namespace, tag string, handle func(*etree.
return nil
}
// NSFindOne conducts a depth-first search for the specified element. If such an element
// is found a reference to it is returned.
// NSFindOne behaves identically to NSFindOneCtx, but uses DefaultNSContext for
// context.
func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error) {
return NSFindOneCtx(DefaultNSContext, el, namespace, tag)
}
// NSFindOneCtx conducts a depth-first search for the specified element. If such an element
// is found a reference to it is returned.
func NSFindOneCtx(ctx NSContext, el *etree.Element, namespace, tag string) (*etree.Element, error) {
var found *etree.Element
err := NSFindIterate(el, namespace, tag, func(el *etree.Element) error {
err := NSFindIterateCtx(ctx, el, namespace, tag, func(ctx NSContext, el *etree.Element) error {
found = el
return ErrTraversalHalted
})
@@ -250,3 +308,21 @@ func NSFindOne(el *etree.Element, namespace, tag string) (*etree.Element, error)
return found, nil
}
// NSBuildParentContext recurses upward from an element in order to build an NSContext
// for its immediate parent. If the element has no parent DefaultNSContext
// is returned.
func NSBuildParentContext(el *etree.Element) (NSContext, error) {
parent := el.Parent()
if parent == nil {
return DefaultNSContext, nil
}
ctx, err := NSBuildParentContext(parent)
if err != nil {
return ctx, err
}
return ctx.SubContext(parent)
}

View File

@@ -2,6 +2,8 @@ package etreeutils
import "github.com/beevik/etree"
// SortedAttrs provides sorting capabilities, compatible with XML C14N, on top
// of an []etree.Attr
type SortedAttrs []etree.Attr
func (a SortedAttrs) Len() int {
@@ -13,17 +15,23 @@ func (a SortedAttrs) Swap(i, j int) {
}
func (a SortedAttrs) Less(i, j int) bool {
// As I understand it: any "xmlns" attribute should come first, followed by any
// any "xmlns:prefix" attributes, presumably ordered by prefix. Lastly any other
// attributes in lexicographical order.
if a[i].Space == defaultPrefix && a[i].Key == xmlnsPrefix {
return true
}
// This is the best reference I've found on sort order:
// http://dst.lbl.gov/~ksb/Scratch/XMLC14N.html
// If attr j is a default namespace declaration, attr i may
// not be strictly "less" than it.
if a[j].Space == defaultPrefix && a[j].Key == xmlnsPrefix {
return false
}
// Otherwise, if attr i is a default namespace declaration, it
// must be less than anything else.
if a[i].Space == defaultPrefix && a[i].Key == xmlnsPrefix {
return true
}
// Next, namespace prefix declarations, sorted by prefix, come before
// anythign else.
if a[i].Space == xmlnsPrefix {
if a[j].Space == xmlnsPrefix {
return a[i].Key < a[j].Key
@@ -35,6 +43,21 @@ func (a SortedAttrs) Less(i, j int) bool {
return false
}
// Then come unprefixed attributes, sorted by key.
if a[i].Space == defaultPrefix {
if a[j].Space == defaultPrefix {
return a[i].Key < a[j].Key
}
return true
}
if a[j].Space == defaultPrefix {
return false
}
// Wow. We're still going. Finally, attributes in the same namespace should be
// sorted by key. Attributes in different namespaces should be sorted by the
// actual namespace (_not_ the prefix). For now just use the prefix.
if a[i].Space == a[j].Space {
return a[i].Key < a[j].Key
}

View File

@@ -0,0 +1,43 @@
package etreeutils
import (
"encoding/xml"
"github.com/beevik/etree"
)
// NSUnmarshalElement unmarshals the passed etree Element into the value pointed to by
// v using encoding/xml in the context of the passed NSContext. If v implements
// ElementKeeper, SetUnderlyingElement will be called on v with a reference to el.
func NSUnmarshalElement(ctx NSContext, el *etree.Element, v interface{}) error {
detatched, err := NSDetatch(ctx, el)
if err != nil {
return err
}
doc := etree.NewDocument()
doc.AddChild(detatched)
data, err := doc.WriteToBytes()
if err != nil {
return err
}
err = xml.Unmarshal(data, v)
if err != nil {
return err
}
switch v := v.(type) {
case ElementKeeper:
v.SetUnderlyingElement(el)
}
return nil
}
// ElementKeeper should be implemented by types which will be passed to
// UnmarshalElement, but wish to keep a reference
type ElementKeeper interface {
SetUnderlyingElement(*etree.Element)
UnderlyingElement() *etree.Element
}