github.com/matislovas/ratago@v0.0.0-20240408115641-cc0857415a7a/xslt/sort.go (about)

     1  package xslt
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/matislovas/gokogiri/xml"
     7  )
     8  
     9  func (i *XsltInstruction) Sort(nodes []xml.Node, context *ExecutionContext) {
    10  	ns := &NodeSorter{nodes, i.sorting, context}
    11  	sort.Sort(ns)
    12  }
    13  
    14  type NodeSorter struct {
    15  	nodes   []xml.Node
    16  	crit    []*sortCriteria
    17  	context *ExecutionContext
    18  }
    19  
    20  func (s *NodeSorter) Len() int {
    21  	return len(s.nodes)
    22  }
    23  
    24  func (s *NodeSorter) Swap(i, j int) {
    25  	s.nodes[i], s.nodes[j] = s.nodes[j], s.nodes[i]
    26  }
    27  
    28  func (s *NodeSorter) Less(i, j int) bool {
    29  	//make this loop through the by array
    30  	// return as soon as non-equal result
    31  	return execSortFunction(s.nodes[i], s.nodes[j], s.crit, s.context)
    32  }
    33  
    34  type sortCriteria struct {
    35  	sel     string
    36  	reverse bool
    37  	numeric bool
    38  }
    39  
    40  func compileSortFunction(i *XsltInstruction) (s *sortCriteria) {
    41  	s = new(sortCriteria)
    42  	s.sel = i.Node.Attr("select")
    43  	if s.sel == "" {
    44  		s.sel = "string(.)"
    45  	}
    46  	order := i.Node.Attr("order")
    47  	if order == "descending" {
    48  		s.reverse = true
    49  	}
    50  	datatype := i.Node.Attr("data-type")
    51  	if datatype == "number" {
    52  		s.numeric = true
    53  	}
    54  	return s
    55  }
    56  
    57  func execSortFunction(n1, n2 xml.Node, crits []*sortCriteria, context *ExecutionContext) bool {
    58  
    59  	for _, crit := range crits {
    60  		if crit.numeric {
    61  			if execNumericSortFunction(n1, n2, crit, context) {
    62  				return true
    63  			}
    64  			continue
    65  		}
    66  
    67  		s1, _ := context.EvalXPath(n1, crit.sel)
    68  		s1, _ = context.XPathContext.ResultAsString()
    69  		s2, _ := context.EvalXPath(n2, crit.sel)
    70  		s2, _ = context.XPathContext.ResultAsString()
    71  
    72  		if s1.(string) == s2.(string) {
    73  			continue
    74  		}
    75  
    76  		if !crit.reverse {
    77  			return s1.(string) < s2.(string)
    78  		} else {
    79  			return s1.(string) > s2.(string)
    80  		}
    81  	}
    82  	return false
    83  	//case-order
    84  	//lang
    85  	//data-type (text)
    86  }
    87  func execNumericSortFunction(n1, n2 xml.Node, crit *sortCriteria, context *ExecutionContext) bool {
    88  
    89  	s1, _ := context.EvalXPath(n1, crit.sel)
    90  	s1, _ = context.XPathContext.ResultAsNumber()
    91  	s2, _ := context.EvalXPath(n2, crit.sel)
    92  	s2, _ = context.XPathContext.ResultAsNumber()
    93  
    94  	if !crit.reverse {
    95  		if s1.(float64) < s2.(float64) {
    96  			return true
    97  		}
    98  	} else {
    99  		if s1.(float64) > s2.(float64) {
   100  			return true
   101  		}
   102  	}
   103  	return false
   104  }
   105  
   106  /*
   107  package collate_test
   108  
   109  import (
   110          "fmt"
   111          "testing"
   112  
   113          "code.google.com/p/go.text/collate"
   114          "code.google.com/p/go.text/language"
   115  )
   116  
   117  func ExampleCollator_Strings() {
   118          c := collate.New(language.Und)
   119          strings := []string{
   120                  "ad",
   121                  "ab",
   122                  "äb",
   123                  "ac",
   124          }
   125          c.SortStrings(strings)
   126          fmt.Println(strings)
   127          // Output: [ab äb ac ad]
   128      }
   129  
   130      type sorter []string
   131  
   132      func (s sorter) Len() int {
   133          return len(s)
   134      }
   135  
   136      func (s sorter) Swap(i, j int) {
   137          s[j], s[i] = s[i], s[j]
   138      }
   139  
   140      func (s sorter) Bytes(i int) []byte {
   141          return []byte(s[i])
   142      }
   143  
   144      func TestSort(t *testing.T) {
   145          c := collate.New(language.En)
   146          strings := []string{
   147              "bcd",
   148              "abc",
   149              "ddd",
   150          }
   151          c.Sort(sorter(strings))
   152          res := fmt.Sprint(strings)
   153          want := "[abc bcd ddd]"
   154          if res != want {
   155                  t.Errorf("found %s; want %s", res, want)
   156          }
   157  }
   158  */