gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

xmlfmt.go (2685B)


      1 ////////////////////////////////////////////////////////////////////////////
      2 // Porgram: xmlfmt.go
      3 // Purpose: Go XML Beautify from XML string using pure string manipulation
      4 // Authors: Antonio Sun (c) 2016-2021, All rights reserved
      5 ////////////////////////////////////////////////////////////////////////////
      6 
      7 package xmlfmt
      8 
      9 import (
     10 	"html"
     11 	"regexp"
     12 	"strings"
     13 )
     14 
     15 var (
     16 	reg = regexp.MustCompile(`<([/!]?)([^>]+?)(/?)>`)
     17 	// NL is the newline string used in XML output, define for DOS-convenient.
     18 	NL = "\r\n"
     19 )
     20 
     21 // FormatXML will (purly) reformat the XML string in a readable way, without any rewriting/altering the structure.
     22 // If your XML Comments have nested tags in them, or you're not 100% sure otherwise, pass `true` as the third parameter to this function. But don't turn it on blindly, as the code has become ten times more complicated because of it.
     23 func FormatXML(xmls, prefix, indent string, nestedTagsInComments ...bool) string {
     24 	nestedTagsInComment := false
     25 	if len(nestedTagsInComments) > 0 {
     26 		nestedTagsInComment = nestedTagsInComments[0]
     27 	}
     28 	reXmlComments := regexp.MustCompile(`(?s)(<!--)(.*?)(-->)`)
     29 	src := regexp.MustCompile(`(?s)>\s+<`).ReplaceAllString(xmls, "><")
     30 	if nestedTagsInComment {
     31 		src = reXmlComments.ReplaceAllStringFunc(src, func(m string) string {
     32 			parts := reXmlComments.FindStringSubmatch(m)
     33 			p2 := regexp.MustCompile(`\r*\n`).ReplaceAllString(parts[2], " ")
     34 			return parts[1] + html.EscapeString(p2) + parts[3]
     35 		})
     36 	}
     37 	rf := replaceTag(prefix, indent)
     38 	r := prefix + reg.ReplaceAllStringFunc(src, rf)
     39 	if nestedTagsInComment {
     40 		r = reXmlComments.ReplaceAllStringFunc(r, func(m string) string {
     41 			parts := reXmlComments.FindStringSubmatch(m)
     42 			return parts[1] + html.UnescapeString(parts[2]) + parts[3]
     43 		})
     44 	}
     45 
     46 	return r
     47 }
     48 
     49 // replaceTag returns a closure function to do 's/(?<=>)\s+(?=<)//g; s(<(/?)([^>]+?)(/?)>)($indent+=$3?0:$1?-1:1;"<$1$2$3>"."\n".("  "x$indent))ge' as in Perl
     50 // and deal with comments as well
     51 func replaceTag(prefix, indent string) func(string) string {
     52 	indentLevel := 0
     53 	return func(m string) string {
     54 		// head elem
     55 		if strings.HasPrefix(m, "<?xml") {
     56 			return NL + prefix + strings.Repeat(indent, indentLevel) + m
     57 		}
     58 		// empty elem
     59 		if strings.HasSuffix(m, "/>") {
     60 			return NL + prefix + strings.Repeat(indent, indentLevel) + m
     61 		}
     62 		// comment elem
     63 		if strings.HasPrefix(m, "<!") {
     64 			return NL + prefix + strings.Repeat(indent, indentLevel) + m
     65 		}
     66 		// end elem
     67 		if strings.HasPrefix(m, "</") {
     68 			indentLevel--
     69 			return NL + prefix + strings.Repeat(indent, indentLevel) + m
     70 		}
     71 		defer func() {
     72 			indentLevel++
     73 		}()
     74 
     75 		return NL + prefix + strings.Repeat(indent, indentLevel) + m
     76 	}
     77 }