Update go dependencies
This commit is contained in:
parent
f9624cbe46
commit
307bf76454
280 changed files with 54728 additions and 2991 deletions
1
vendor/github.com/yudai/gojsondiff/.gitignore
generated
vendored
Normal file
1
vendor/github.com/yudai/gojsondiff/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
*.test
|
||||
145
vendor/github.com/yudai/gojsondiff/LICENSE
generated
vendored
Normal file
145
vendor/github.com/yudai/gojsondiff/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Iwasaki Yudai
|
||||
|
||||
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.
|
||||
|
||||
============================================================================
|
||||
|
||||
This repository is build with following third party libraries. Thank you!
|
||||
|
||||
## go-diff - https://github.com/sergi/go-diff
|
||||
|
||||
Copyright (c) 2012 Sergi Mansilla <sergi.mansilla@gmail.com>
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## golcs - https://github.com/yudai/golcs
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Iwasaki Yudai
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## cli.go - https://github.com/codegangsta/cli
|
||||
|
||||
Copyright (C) 2013 Jeremy Saenz
|
||||
All Rights Reserved.
|
||||
|
||||
MIT LICENSE
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## ginkgo - https://github.com/onsi/ginkgo
|
||||
|
||||
Copyright (c) 2013-2014 Onsi Fakhouri
|
||||
|
||||
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.
|
||||
|
||||
|
||||
# gomega - https://github.com/onsi/gomega
|
||||
|
||||
Copyright (c) 2013-2014 Onsi Fakhouri
|
||||
|
||||
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.
|
||||
5
vendor/github.com/yudai/gojsondiff/Makefile
generated
vendored
Normal file
5
vendor/github.com/yudai/gojsondiff/Makefile
generated
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
test:
|
||||
if [ `go fmt $(go list ./... | grep -v /vendor/) | wc -l` -gt 0 ]; then echo "go fmt error"; exit 1; fi
|
||||
|
||||
tools:
|
||||
go get github.com/tools/godep
|
||||
157
vendor/github.com/yudai/gojsondiff/README.md
generated
vendored
Normal file
157
vendor/github.com/yudai/gojsondiff/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
# Go JSON Diff (and Patch)
|
||||
|
||||
[][wercker]
|
||||
[][godoc]
|
||||
[][license]
|
||||
|
||||
[wercker]: https://app.wercker.com/project/bykey/00d70daaf40ce277fd4f10290f097b9d
|
||||
[godoc]: https://godoc.org/github.com/yudai/gojsondiff
|
||||
[license]: https://github.com/yudai/gojsondiff/blob/master/LICENSE
|
||||
|
||||
## How to use
|
||||
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
go get github.com/yudai/gojsondiff
|
||||
```
|
||||
|
||||
### Comparing two JSON strings
|
||||
|
||||
See `jd/main.go` for how to use this library.
|
||||
|
||||
|
||||
## CLI tool
|
||||
|
||||
This repository contains a package that you can use as a CLI tool.
|
||||
|
||||
### Installation
|
||||
|
||||
```sh
|
||||
go get github.com/yudai/gojsondiff/jd
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
#### Diff
|
||||
|
||||
Just give two json files to the `jd` command:
|
||||
|
||||
```sh
|
||||
jd one.json another.json
|
||||
```
|
||||
|
||||
Outputs would be something like:
|
||||
|
||||
```diff
|
||||
{
|
||||
"arr": [
|
||||
0: "arr0",
|
||||
1: 21,
|
||||
2: {
|
||||
"num": 1,
|
||||
- "str": "pek3f"
|
||||
+ "str": "changed"
|
||||
},
|
||||
3: [
|
||||
0: 0,
|
||||
- 1: "1"
|
||||
+ 1: "changed"
|
||||
]
|
||||
],
|
||||
"bool": true,
|
||||
"num_float": 39.39,
|
||||
"num_int": 13,
|
||||
"obj": {
|
||||
"arr": [
|
||||
0: 17,
|
||||
1: "str",
|
||||
2: {
|
||||
- "str": "eafeb"
|
||||
+ "str": "changed"
|
||||
}
|
||||
],
|
||||
+ "new": "added",
|
||||
- "num": 19,
|
||||
"obj": {
|
||||
- "num": 14,
|
||||
+ "num": 9999
|
||||
- "str": "efj3"
|
||||
+ "str": "changed"
|
||||
},
|
||||
"str": "bcded"
|
||||
},
|
||||
"str": "abcde"
|
||||
}
|
||||
```
|
||||
|
||||
When you prefer the delta foramt of [jsondiffpatch](https://github.com/benjamine/jsondiffpatch), add the `-f delta` option.
|
||||
|
||||
```sh
|
||||
jd -f delta one.json another.json
|
||||
```
|
||||
|
||||
This command shows:
|
||||
|
||||
```json
|
||||
{
|
||||
"arr": {
|
||||
"2": {
|
||||
"str": [
|
||||
"pek3f",
|
||||
"changed"
|
||||
]
|
||||
},
|
||||
"3": {
|
||||
"1": [
|
||||
"1",
|
||||
"changed"
|
||||
],
|
||||
"_t": "a"
|
||||
},
|
||||
"_t": "a"
|
||||
},
|
||||
"obj": {
|
||||
"arr": {
|
||||
"2": {
|
||||
"str": [
|
||||
"eafeb",
|
||||
"changed"
|
||||
]
|
||||
},
|
||||
"_t": "a"
|
||||
},
|
||||
"new": [
|
||||
"added"
|
||||
],
|
||||
"num": [
|
||||
19,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"obj": {
|
||||
"num": [
|
||||
14,
|
||||
9999
|
||||
],
|
||||
"str": [
|
||||
"efj3",
|
||||
"changed"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Patch
|
||||
|
||||
Give a diff file in the delta format and the JSON file to the `jp` command.
|
||||
|
||||
```sh
|
||||
jp diff.delta one.json
|
||||
```
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT License (see `LICENSE` for detail)
|
||||
461
vendor/github.com/yudai/gojsondiff/deltas.go
generated
vendored
Normal file
461
vendor/github.com/yudai/gojsondiff/deltas.go
generated
vendored
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
package gojsondiff
|
||||
|
||||
import (
|
||||
"errors"
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"reflect"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// A Delta represents an atomic difference between two JSON objects.
|
||||
type Delta interface {
|
||||
// Similarity calculates the similarity of the Delta values.
|
||||
// The return value is normalized from 0 to 1,
|
||||
// 0 is completely different and 1 is they are same
|
||||
Similarity() (similarity float64)
|
||||
}
|
||||
|
||||
// To cache the calculated similarity,
|
||||
// concrete Deltas can use similariter and similarityCache
|
||||
type similariter interface {
|
||||
similarity() (similarity float64)
|
||||
}
|
||||
|
||||
type similarityCache struct {
|
||||
similariter
|
||||
value float64
|
||||
}
|
||||
|
||||
func newSimilarityCache(sim similariter) similarityCache {
|
||||
cache := similarityCache{similariter: sim, value: -1}
|
||||
return cache
|
||||
}
|
||||
|
||||
func (cache similarityCache) Similarity() (similarity float64) {
|
||||
if cache.value < 0 {
|
||||
cache.value = cache.similariter.similarity()
|
||||
}
|
||||
return cache.value
|
||||
}
|
||||
|
||||
// A Position represents the position of a Delta in an object or an array.
|
||||
type Position interface {
|
||||
// String returns the position as a string
|
||||
String() (name string)
|
||||
|
||||
// CompareTo returns a true if the Position is smaller than another Position.
|
||||
// This function is used to sort Positions by the sort package.
|
||||
CompareTo(another Position) bool
|
||||
}
|
||||
|
||||
// A Name is a Postition with a string, which means the delta is in an object.
|
||||
type Name string
|
||||
|
||||
func (n Name) String() (name string) {
|
||||
return string(n)
|
||||
}
|
||||
|
||||
func (n Name) CompareTo(another Position) bool {
|
||||
return n < another.(Name)
|
||||
}
|
||||
|
||||
// A Index is a Position with an int value, which means the Delta is in an Array.
|
||||
type Index int
|
||||
|
||||
func (i Index) String() (name string) {
|
||||
return strconv.Itoa(int(i))
|
||||
}
|
||||
|
||||
func (i Index) CompareTo(another Position) bool {
|
||||
return i < another.(Index)
|
||||
}
|
||||
|
||||
// A PreDelta is a Delta that has a position of the left side JSON object.
|
||||
// Deltas implements this interface should be applies before PostDeltas.
|
||||
type PreDelta interface {
|
||||
// PrePosition returns the Position.
|
||||
PrePosition() Position
|
||||
|
||||
// PreApply applies the delta to object.
|
||||
PreApply(object interface{}) interface{}
|
||||
}
|
||||
|
||||
type preDelta struct{ Position }
|
||||
|
||||
func (i preDelta) PrePosition() Position {
|
||||
return Position(i.Position)
|
||||
}
|
||||
|
||||
type preDeltas []PreDelta
|
||||
|
||||
// for sorting
|
||||
func (s preDeltas) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// for sorting
|
||||
func (s preDeltas) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// for sorting
|
||||
func (s preDeltas) Less(i, j int) bool {
|
||||
return !s[i].PrePosition().CompareTo(s[j].PrePosition())
|
||||
}
|
||||
|
||||
// A PreDelta is a Delta that has a position of the right side JSON object.
|
||||
// Deltas implements this interface should be applies after PreDeltas.
|
||||
type PostDelta interface {
|
||||
// PostPosition returns the Position.
|
||||
PostPosition() Position
|
||||
|
||||
// PostApply applies the delta to object.
|
||||
PostApply(object interface{}) interface{}
|
||||
}
|
||||
|
||||
type postDelta struct{ Position }
|
||||
|
||||
func (i postDelta) PostPosition() Position {
|
||||
return Position(i.Position)
|
||||
}
|
||||
|
||||
type postDeltas []PostDelta
|
||||
|
||||
// for sorting
|
||||
func (s postDeltas) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
// for sorting
|
||||
func (s postDeltas) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
// for sorting
|
||||
func (s postDeltas) Less(i, j int) bool {
|
||||
return s[i].PostPosition().CompareTo(s[j].PostPosition())
|
||||
}
|
||||
|
||||
// An Object is a Delta that represents an object of JSON
|
||||
type Object struct {
|
||||
postDelta
|
||||
similarityCache
|
||||
|
||||
// Deltas holds internal Deltas
|
||||
Deltas []Delta
|
||||
}
|
||||
|
||||
// NewObject returns an Object
|
||||
func NewObject(position Position, deltas []Delta) *Object {
|
||||
d := Object{postDelta: postDelta{position}, Deltas: deltas}
|
||||
d.similarityCache = newSimilarityCache(&d)
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *Object) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
o := object.(map[string]interface{})
|
||||
n := string(d.PostPosition().(Name))
|
||||
o[n] = applyDeltas(d.Deltas, o[n])
|
||||
case []interface{}:
|
||||
o := object.([]interface{})
|
||||
n := int(d.PostPosition().(Index))
|
||||
o[n] = applyDeltas(d.Deltas, o[n])
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Object) similarity() (similarity float64) {
|
||||
similarity = deltasSimilarity(d.Deltas)
|
||||
return
|
||||
}
|
||||
|
||||
// An Array is a Delta that represents an array of JSON
|
||||
type Array struct {
|
||||
postDelta
|
||||
similarityCache
|
||||
|
||||
// Deltas holds internal Deltas
|
||||
Deltas []Delta
|
||||
}
|
||||
|
||||
// NewArray returns an Array
|
||||
func NewArray(position Position, deltas []Delta) *Array {
|
||||
d := Array{postDelta: postDelta{position}, Deltas: deltas}
|
||||
d.similarityCache = newSimilarityCache(&d)
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *Array) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
o := object.(map[string]interface{})
|
||||
n := string(d.PostPosition().(Name))
|
||||
o[n] = applyDeltas(d.Deltas, o[n])
|
||||
case []interface{}:
|
||||
o := object.([]interface{})
|
||||
n := int(d.PostPosition().(Index))
|
||||
o[n] = applyDeltas(d.Deltas, o[n])
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Array) similarity() (similarity float64) {
|
||||
similarity = deltasSimilarity(d.Deltas)
|
||||
return
|
||||
}
|
||||
|
||||
// An Added represents a new added field of an object or an array
|
||||
type Added struct {
|
||||
postDelta
|
||||
similarityCache
|
||||
|
||||
// Values holds the added value
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewAdded returns a new Added
|
||||
func NewAdded(position Position, value interface{}) *Added {
|
||||
d := Added{postDelta: postDelta{position}, Value: value}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *Added) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
object.(map[string]interface{})[string(d.PostPosition().(Name))] = d.Value
|
||||
case []interface{}:
|
||||
i := int(d.PostPosition().(Index))
|
||||
o := object.([]interface{})
|
||||
if i < len(o) {
|
||||
o = append(o, 0) //dummy
|
||||
copy(o[i+1:], o[i:])
|
||||
o[i] = d.Value
|
||||
object = o
|
||||
} else {
|
||||
object = append(o, d.Value)
|
||||
}
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Added) similarity() (similarity float64) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// A Modified represents a field whose value is changed.
|
||||
type Modified struct {
|
||||
postDelta
|
||||
similarityCache
|
||||
|
||||
// The value before modification
|
||||
OldValue interface{}
|
||||
|
||||
// The value after modification
|
||||
NewValue interface{}
|
||||
}
|
||||
|
||||
// NewModified returns a Modified
|
||||
func NewModified(position Position, oldValue, newValue interface{}) *Modified {
|
||||
d := Modified{
|
||||
postDelta: postDelta{position},
|
||||
OldValue: oldValue,
|
||||
NewValue: newValue,
|
||||
}
|
||||
d.similarityCache = newSimilarityCache(&d)
|
||||
return &d
|
||||
|
||||
}
|
||||
|
||||
func (d *Modified) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
// TODO check old value
|
||||
object.(map[string]interface{})[string(d.PostPosition().(Name))] = d.NewValue
|
||||
case []interface{}:
|
||||
object.([]interface{})[int(d.PostPosition().(Index))] = d.NewValue
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Modified) similarity() (similarity float64) {
|
||||
similarity += 0.3 // at least, they are at the same position
|
||||
if reflect.TypeOf(d.OldValue) == reflect.TypeOf(d.NewValue) {
|
||||
similarity += 0.3 // types are same
|
||||
|
||||
switch d.OldValue.(type) {
|
||||
case string:
|
||||
similarity += 0.4 * stringSimilarity(d.OldValue.(string), d.NewValue.(string))
|
||||
case float64:
|
||||
ratio := d.OldValue.(float64) / d.NewValue.(float64)
|
||||
if ratio > 1 {
|
||||
ratio = 1 / ratio
|
||||
}
|
||||
similarity += 0.4 * ratio
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// A TextDiff represents a Modified with TextDiff between the old and the new values.
|
||||
type TextDiff struct {
|
||||
Modified
|
||||
|
||||
// Diff string
|
||||
Diff []dmp.Patch
|
||||
}
|
||||
|
||||
// NewTextDiff returns
|
||||
func NewTextDiff(position Position, diff []dmp.Patch, oldValue, newValue interface{}) *TextDiff {
|
||||
d := TextDiff{
|
||||
Modified: *NewModified(position, oldValue, newValue),
|
||||
Diff: diff,
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *TextDiff) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
o := object.(map[string]interface{})
|
||||
i := string(d.PostPosition().(Name))
|
||||
// TODO error
|
||||
d.OldValue = o[i]
|
||||
// TODO error
|
||||
d.patch()
|
||||
o[i] = d.NewValue
|
||||
case []interface{}:
|
||||
o := object.([]interface{})
|
||||
i := d.PostPosition().(Index)
|
||||
d.OldValue = o[i]
|
||||
// TODO error
|
||||
d.patch()
|
||||
o[i] = d.NewValue
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *TextDiff) patch() error {
|
||||
if d.OldValue == nil {
|
||||
return errors.New("Old Value is not set")
|
||||
}
|
||||
patcher := dmp.New()
|
||||
patched, successes := patcher.PatchApply(d.Diff, d.OldValue.(string))
|
||||
for _, success := range successes {
|
||||
if !success {
|
||||
return errors.New("Failed to apply a patch")
|
||||
}
|
||||
}
|
||||
d.NewValue = patched
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *TextDiff) DiffString() string {
|
||||
dmp := dmp.New()
|
||||
return dmp.PatchToText(d.Diff)
|
||||
}
|
||||
|
||||
// A Delted represents deleted field or index of an Object or an Array.
|
||||
type Deleted struct {
|
||||
preDelta
|
||||
|
||||
// The value deleted
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
// NewDeleted returns a Deleted
|
||||
func NewDeleted(position Position, value interface{}) *Deleted {
|
||||
d := Deleted{
|
||||
preDelta: preDelta{position},
|
||||
Value: value,
|
||||
}
|
||||
return &d
|
||||
|
||||
}
|
||||
|
||||
func (d *Deleted) PreApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
// TODO check old value
|
||||
delete(object.(map[string]interface{}), string(d.PrePosition().(Name)))
|
||||
case []interface{}:
|
||||
i := int(d.PrePosition().(Index))
|
||||
o := object.([]interface{})
|
||||
object = append(o[:i], o[i+1:]...)
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d Deleted) Similarity() (similarity float64) {
|
||||
return 0
|
||||
}
|
||||
|
||||
// A Moved represents field that is moved, which means the index or name is
|
||||
// changed. Note that, in this library, assigning a Moved and a Modified to
|
||||
// a single position is not allowed. For the compatibility with jsondiffpatch,
|
||||
// the Moved in this library can hold the old and new value in it.
|
||||
type Moved struct {
|
||||
preDelta
|
||||
postDelta
|
||||
similarityCache
|
||||
// The value before moving
|
||||
Value interface{}
|
||||
// The delta applied after moving (for compatibility)
|
||||
Delta interface{}
|
||||
}
|
||||
|
||||
func NewMoved(oldPosition Position, newPosition Position, value interface{}, delta Delta) *Moved {
|
||||
d := Moved{
|
||||
preDelta: preDelta{oldPosition},
|
||||
postDelta: postDelta{newPosition},
|
||||
Value: value,
|
||||
Delta: delta,
|
||||
}
|
||||
d.similarityCache = newSimilarityCache(&d)
|
||||
return &d
|
||||
}
|
||||
|
||||
func (d *Moved) PreApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
//not supported
|
||||
case []interface{}:
|
||||
i := int(d.PrePosition().(Index))
|
||||
o := object.([]interface{})
|
||||
d.Value = o[i]
|
||||
object = append(o[:i], o[i+1:]...)
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Moved) PostApply(object interface{}) interface{} {
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
//not supported
|
||||
case []interface{}:
|
||||
i := int(d.PostPosition().(Index))
|
||||
o := object.([]interface{})
|
||||
o = append(o, 0) //dummy
|
||||
copy(o[i+1:], o[i:])
|
||||
o[i] = d.Value
|
||||
object = o
|
||||
}
|
||||
|
||||
if d.Delta != nil {
|
||||
d.Delta.(PostDelta).PostApply(object)
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (d *Moved) similarity() (similarity float64) {
|
||||
similarity = 0.6 // as type and contens are same
|
||||
ratio := float64(d.PrePosition().(Index)) / float64(d.PostPosition().(Index))
|
||||
if ratio > 1 {
|
||||
ratio = 1 / ratio
|
||||
}
|
||||
similarity += 0.4 * ratio
|
||||
return
|
||||
}
|
||||
370
vendor/github.com/yudai/gojsondiff/formatter/ascii.go
generated
vendored
Normal file
370
vendor/github.com/yudai/gojsondiff/formatter/ascii.go
generated
vendored
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
diff "github.com/yudai/gojsondiff"
|
||||
)
|
||||
|
||||
func NewAsciiFormatter(left interface{}, config AsciiFormatterConfig) *AsciiFormatter {
|
||||
return &AsciiFormatter{
|
||||
left: left,
|
||||
config: config,
|
||||
}
|
||||
}
|
||||
|
||||
type AsciiFormatter struct {
|
||||
left interface{}
|
||||
config AsciiFormatterConfig
|
||||
buffer *bytes.Buffer
|
||||
path []string
|
||||
size []int
|
||||
inArray []bool
|
||||
line *AsciiLine
|
||||
}
|
||||
|
||||
type AsciiFormatterConfig struct {
|
||||
ShowArrayIndex bool
|
||||
Coloring bool
|
||||
}
|
||||
|
||||
var AsciiFormatterDefaultConfig = AsciiFormatterConfig{}
|
||||
|
||||
type AsciiLine struct {
|
||||
marker string
|
||||
indent int
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) Format(diff diff.Diff) (result string, err error) {
|
||||
f.buffer = bytes.NewBuffer([]byte{})
|
||||
f.path = []string{}
|
||||
f.size = []int{}
|
||||
f.inArray = []bool{}
|
||||
|
||||
if v, ok := f.left.(map[string]interface{}); ok {
|
||||
f.formatObject(v, diff)
|
||||
} else if v, ok := f.left.([]interface{}); ok {
|
||||
f.formatArray(v, diff)
|
||||
} else {
|
||||
return "", fmt.Errorf("expected map[string]interface{} or []interface{}, got %T",
|
||||
f.left)
|
||||
}
|
||||
|
||||
return f.buffer.String(), nil
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) formatObject(left map[string]interface{}, df diff.Diff) {
|
||||
f.addLineWith(AsciiSame, "{")
|
||||
f.push("ROOT", len(left), false)
|
||||
f.processObject(left, df.Deltas())
|
||||
f.pop()
|
||||
f.addLineWith(AsciiSame, "}")
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) formatArray(left []interface{}, df diff.Diff) {
|
||||
f.addLineWith(AsciiSame, "[")
|
||||
f.push("ROOT", len(left), true)
|
||||
f.processArray(left, df.Deltas())
|
||||
f.pop()
|
||||
f.addLineWith(AsciiSame, "]")
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) processArray(array []interface{}, deltas []diff.Delta) error {
|
||||
patchedIndex := 0
|
||||
for index, value := range array {
|
||||
f.processItem(value, deltas, diff.Index(index))
|
||||
patchedIndex++
|
||||
}
|
||||
|
||||
// additional Added
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case *diff.Added:
|
||||
d := delta.(*diff.Added)
|
||||
// skip items already processed
|
||||
if int(d.Position.(diff.Index)) < len(array) {
|
||||
continue
|
||||
}
|
||||
f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) processObject(object map[string]interface{}, deltas []diff.Delta) error {
|
||||
names := sortedKeys(object)
|
||||
for _, name := range names {
|
||||
value := object[name]
|
||||
f.processItem(value, deltas, diff.Name(name))
|
||||
}
|
||||
|
||||
// Added
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case *diff.Added:
|
||||
d := delta.(*diff.Added)
|
||||
f.printRecursive(d.Position.String(), d.Value, AsciiAdded)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) processItem(value interface{}, deltas []diff.Delta, position diff.Position) error {
|
||||
matchedDeltas := f.searchDeltas(deltas, position)
|
||||
positionStr := position.String()
|
||||
if len(matchedDeltas) > 0 {
|
||||
for _, matchedDelta := range matchedDeltas {
|
||||
|
||||
switch matchedDelta.(type) {
|
||||
case *diff.Object:
|
||||
d := matchedDelta.(*diff.Object)
|
||||
switch value.(type) {
|
||||
case map[string]interface{}:
|
||||
//ok
|
||||
default:
|
||||
return errors.New("Type mismatch")
|
||||
}
|
||||
o := value.(map[string]interface{})
|
||||
|
||||
f.newLine(AsciiSame)
|
||||
f.printKey(positionStr)
|
||||
f.print("{")
|
||||
f.closeLine()
|
||||
f.push(positionStr, len(o), false)
|
||||
f.processObject(o, d.Deltas)
|
||||
f.pop()
|
||||
f.newLine(AsciiSame)
|
||||
f.print("}")
|
||||
f.printComma()
|
||||
f.closeLine()
|
||||
|
||||
case *diff.Array:
|
||||
d := matchedDelta.(*diff.Array)
|
||||
switch value.(type) {
|
||||
case []interface{}:
|
||||
//ok
|
||||
default:
|
||||
return errors.New("Type mismatch")
|
||||
}
|
||||
a := value.([]interface{})
|
||||
|
||||
f.newLine(AsciiSame)
|
||||
f.printKey(positionStr)
|
||||
f.print("[")
|
||||
f.closeLine()
|
||||
f.push(positionStr, len(a), true)
|
||||
f.processArray(a, d.Deltas)
|
||||
f.pop()
|
||||
f.newLine(AsciiSame)
|
||||
f.print("]")
|
||||
f.printComma()
|
||||
f.closeLine()
|
||||
|
||||
case *diff.Added:
|
||||
d := matchedDelta.(*diff.Added)
|
||||
f.printRecursive(positionStr, d.Value, AsciiAdded)
|
||||
f.size[len(f.size)-1]++
|
||||
|
||||
case *diff.Modified:
|
||||
d := matchedDelta.(*diff.Modified)
|
||||
savedSize := f.size[len(f.size)-1]
|
||||
f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
|
||||
f.size[len(f.size)-1] = savedSize
|
||||
f.printRecursive(positionStr, d.NewValue, AsciiAdded)
|
||||
|
||||
case *diff.TextDiff:
|
||||
savedSize := f.size[len(f.size)-1]
|
||||
d := matchedDelta.(*diff.TextDiff)
|
||||
f.printRecursive(positionStr, d.OldValue, AsciiDeleted)
|
||||
f.size[len(f.size)-1] = savedSize
|
||||
f.printRecursive(positionStr, d.NewValue, AsciiAdded)
|
||||
|
||||
case *diff.Deleted:
|
||||
d := matchedDelta.(*diff.Deleted)
|
||||
f.printRecursive(positionStr, d.Value, AsciiDeleted)
|
||||
|
||||
default:
|
||||
return errors.New("Unknown Delta type detected")
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
f.printRecursive(positionStr, value, AsciiSame)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) searchDeltas(deltas []diff.Delta, postion diff.Position) (results []diff.Delta) {
|
||||
results = make([]diff.Delta, 0)
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case diff.PostDelta:
|
||||
if delta.(diff.PostDelta).PostPosition() == postion {
|
||||
results = append(results, delta)
|
||||
}
|
||||
case diff.PreDelta:
|
||||
if delta.(diff.PreDelta).PrePosition() == postion {
|
||||
results = append(results, delta)
|
||||
}
|
||||
default:
|
||||
panic("heh")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
AsciiSame = " "
|
||||
AsciiAdded = "+"
|
||||
AsciiDeleted = "-"
|
||||
)
|
||||
|
||||
var AsciiStyles = map[string]string{
|
||||
AsciiAdded: "30;42",
|
||||
AsciiDeleted: "30;41",
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) push(name string, size int, array bool) {
|
||||
f.path = append(f.path, name)
|
||||
f.size = append(f.size, size)
|
||||
f.inArray = append(f.inArray, array)
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) pop() {
|
||||
f.path = f.path[0 : len(f.path)-1]
|
||||
f.size = f.size[0 : len(f.size)-1]
|
||||
f.inArray = f.inArray[0 : len(f.inArray)-1]
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) addLineWith(marker string, value string) {
|
||||
f.line = &AsciiLine{
|
||||
marker: marker,
|
||||
indent: len(f.path),
|
||||
buffer: bytes.NewBufferString(value),
|
||||
}
|
||||
f.closeLine()
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) newLine(marker string) {
|
||||
f.line = &AsciiLine{
|
||||
marker: marker,
|
||||
indent: len(f.path),
|
||||
buffer: bytes.NewBuffer([]byte{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) closeLine() {
|
||||
style, ok := AsciiStyles[f.line.marker]
|
||||
if f.config.Coloring && ok {
|
||||
f.buffer.WriteString("\x1b[" + style + "m")
|
||||
}
|
||||
|
||||
f.buffer.WriteString(f.line.marker)
|
||||
for n := 0; n < f.line.indent; n++ {
|
||||
f.buffer.WriteString(" ")
|
||||
}
|
||||
f.buffer.Write(f.line.buffer.Bytes())
|
||||
|
||||
if f.config.Coloring && ok {
|
||||
f.buffer.WriteString("\x1b[0m")
|
||||
}
|
||||
|
||||
f.buffer.WriteRune('\n')
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) printKey(name string) {
|
||||
if !f.inArray[len(f.inArray)-1] {
|
||||
fmt.Fprintf(f.line.buffer, `"%s": `, name)
|
||||
} else if f.config.ShowArrayIndex {
|
||||
fmt.Fprintf(f.line.buffer, `%s: `, name)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) printComma() {
|
||||
f.size[len(f.size)-1]--
|
||||
if f.size[len(f.size)-1] > 0 {
|
||||
f.line.buffer.WriteRune(',')
|
||||
}
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) printValue(value interface{}) {
|
||||
switch value.(type) {
|
||||
case string:
|
||||
fmt.Fprintf(f.line.buffer, `"%s"`, value)
|
||||
case nil:
|
||||
f.line.buffer.WriteString("null")
|
||||
default:
|
||||
fmt.Fprintf(f.line.buffer, `%#v`, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) print(a string) {
|
||||
f.line.buffer.WriteString(a)
|
||||
}
|
||||
|
||||
func (f *AsciiFormatter) printRecursive(name string, value interface{}, marker string) {
|
||||
switch value.(type) {
|
||||
case map[string]interface{}:
|
||||
f.newLine(marker)
|
||||
f.printKey(name)
|
||||
f.print("{")
|
||||
f.closeLine()
|
||||
|
||||
m := value.(map[string]interface{})
|
||||
size := len(m)
|
||||
f.push(name, size, false)
|
||||
|
||||
keys := sortedKeys(m)
|
||||
for _, key := range keys {
|
||||
f.printRecursive(key, m[key], marker)
|
||||
}
|
||||
f.pop()
|
||||
|
||||
f.newLine(marker)
|
||||
f.print("}")
|
||||
f.printComma()
|
||||
f.closeLine()
|
||||
|
||||
case []interface{}:
|
||||
f.newLine(marker)
|
||||
f.printKey(name)
|
||||
f.print("[")
|
||||
f.closeLine()
|
||||
|
||||
s := value.([]interface{})
|
||||
size := len(s)
|
||||
f.push("", size, true)
|
||||
for _, item := range s {
|
||||
f.printRecursive("", item, marker)
|
||||
}
|
||||
f.pop()
|
||||
|
||||
f.newLine(marker)
|
||||
f.print("]")
|
||||
f.printComma()
|
||||
f.closeLine()
|
||||
|
||||
default:
|
||||
f.newLine(marker)
|
||||
f.printKey(name)
|
||||
f.printValue(value)
|
||||
f.printComma()
|
||||
f.closeLine()
|
||||
}
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]interface{}) (keys []string) {
|
||||
keys = make([]string, 0, len(m))
|
||||
for key, _ := range m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return
|
||||
}
|
||||
124
vendor/github.com/yudai/gojsondiff/formatter/delta.go
generated
vendored
Normal file
124
vendor/github.com/yudai/gojsondiff/formatter/delta.go
generated
vendored
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
package formatter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
diff "github.com/yudai/gojsondiff"
|
||||
)
|
||||
|
||||
const (
|
||||
DeltaDelete = 0
|
||||
DeltaTextDiff = 2
|
||||
DeltaMove = 3
|
||||
)
|
||||
|
||||
func NewDeltaFormatter() *DeltaFormatter {
|
||||
return &DeltaFormatter{
|
||||
PrintIndent: true,
|
||||
}
|
||||
}
|
||||
|
||||
type DeltaFormatter struct {
|
||||
PrintIndent bool
|
||||
}
|
||||
|
||||
func (f *DeltaFormatter) Format(diff diff.Diff) (result string, err error) {
|
||||
jsonObject, err := f.formatObject(diff.Deltas())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var resultBytes []byte
|
||||
if f.PrintIndent {
|
||||
resultBytes, err = json.MarshalIndent(jsonObject, "", " ")
|
||||
} else {
|
||||
resultBytes, err = json.Marshal(jsonObject)
|
||||
}
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(resultBytes) + "\n", nil
|
||||
}
|
||||
|
||||
func (f *DeltaFormatter) FormatAsJson(diff diff.Diff) (json map[string]interface{}, err error) {
|
||||
return f.formatObject(diff.Deltas())
|
||||
}
|
||||
|
||||
func (f *DeltaFormatter) formatObject(deltas []diff.Delta) (deltaJson map[string]interface{}, err error) {
|
||||
deltaJson = map[string]interface{}{}
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case *diff.Object:
|
||||
d := delta.(*diff.Object)
|
||||
deltaJson[d.Position.String()], err = f.formatObject(d.Deltas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *diff.Array:
|
||||
d := delta.(*diff.Array)
|
||||
deltaJson[d.Position.String()], err = f.formatArray(d.Deltas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *diff.Added:
|
||||
d := delta.(*diff.Added)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.Value}
|
||||
case *diff.Modified:
|
||||
d := delta.(*diff.Modified)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.OldValue, d.NewValue}
|
||||
case *diff.TextDiff:
|
||||
d := delta.(*diff.TextDiff)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.DiffString(), 0, DeltaTextDiff}
|
||||
case *diff.Deleted:
|
||||
d := delta.(*diff.Deleted)
|
||||
deltaJson[d.PrePosition().String()] = []interface{}{d.Value, 0, DeltaDelete}
|
||||
case *diff.Moved:
|
||||
return nil, errors.New("Delta type 'Move' is not supported in objects")
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Unknown Delta type detected: %#v", delta))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (f *DeltaFormatter) formatArray(deltas []diff.Delta) (deltaJson map[string]interface{}, err error) {
|
||||
deltaJson = map[string]interface{}{
|
||||
"_t": "a",
|
||||
}
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case *diff.Object:
|
||||
d := delta.(*diff.Object)
|
||||
deltaJson[d.Position.String()], err = f.formatObject(d.Deltas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *diff.Array:
|
||||
d := delta.(*diff.Array)
|
||||
deltaJson[d.Position.String()], err = f.formatArray(d.Deltas)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case *diff.Added:
|
||||
d := delta.(*diff.Added)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.Value}
|
||||
case *diff.Modified:
|
||||
d := delta.(*diff.Modified)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.OldValue, d.NewValue}
|
||||
case *diff.TextDiff:
|
||||
d := delta.(*diff.TextDiff)
|
||||
deltaJson[d.PostPosition().String()] = []interface{}{d.DiffString(), 0, DeltaTextDiff}
|
||||
case *diff.Deleted:
|
||||
d := delta.(*diff.Deleted)
|
||||
deltaJson["_"+d.PrePosition().String()] = []interface{}{d.Value, 0, DeltaDelete}
|
||||
case *diff.Moved:
|
||||
d := delta.(*diff.Moved)
|
||||
deltaJson["_"+d.PrePosition().String()] = []interface{}{"", d.PostPosition(), DeltaMove}
|
||||
default:
|
||||
return nil, errors.New(fmt.Sprintf("Unknown Delta type detected: %#v", delta))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
426
vendor/github.com/yudai/gojsondiff/gojsondiff.go
generated
vendored
Normal file
426
vendor/github.com/yudai/gojsondiff/gojsondiff.go
generated
vendored
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
// Package gojsondiff implements "Diff" that compares two JSON objects and
|
||||
// generates Deltas that describes differences between them. The package also
|
||||
// provides "Patch" that apply Deltas to a JSON object.
|
||||
package gojsondiff
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"sort"
|
||||
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"github.com/yudai/golcs"
|
||||
)
|
||||
|
||||
// A Diff holds deltas generated by a Differ
|
||||
type Diff interface {
|
||||
// Deltas returns Deltas that describe differences between two JSON objects
|
||||
Deltas() []Delta
|
||||
// Modified returnes true if Diff has at least one Delta.
|
||||
Modified() bool
|
||||
}
|
||||
|
||||
type diff struct {
|
||||
deltas []Delta
|
||||
}
|
||||
|
||||
func (diff *diff) Deltas() []Delta {
|
||||
return diff.deltas
|
||||
}
|
||||
|
||||
func (diff *diff) Modified() bool {
|
||||
return len(diff.deltas) > 0
|
||||
}
|
||||
|
||||
// A Differ conmapres JSON objects and apply patches
|
||||
type Differ struct {
|
||||
textDiffMinimumLength int
|
||||
}
|
||||
|
||||
// New returns new Differ with default configuration
|
||||
func New() *Differ {
|
||||
return &Differ{
|
||||
textDiffMinimumLength: 30,
|
||||
}
|
||||
}
|
||||
|
||||
// Compare compares two JSON strings as []bytes and return a Diff object.
|
||||
func (differ *Differ) Compare(
|
||||
left []byte,
|
||||
right []byte,
|
||||
) (Diff, error) {
|
||||
var leftMap, rightMap map[string]interface{}
|
||||
err := json.Unmarshal(left, &leftMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(right, &rightMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return differ.CompareObjects(leftMap, rightMap), nil
|
||||
}
|
||||
|
||||
// CompareObjects compares two JSON object as map[string]interface{}
|
||||
// and return a Diff object.
|
||||
func (differ *Differ) CompareObjects(
|
||||
left map[string]interface{},
|
||||
right map[string]interface{},
|
||||
) Diff {
|
||||
deltas := differ.compareMaps(left, right)
|
||||
return &diff{deltas: deltas}
|
||||
}
|
||||
|
||||
// CompareArrays compares two JSON arrays as []interface{}
|
||||
// and return a Diff object.
|
||||
func (differ *Differ) CompareArrays(
|
||||
left []interface{},
|
||||
right []interface{},
|
||||
) Diff {
|
||||
deltas := differ.compareArrays(left, right)
|
||||
return &diff{deltas: deltas}
|
||||
}
|
||||
|
||||
func (differ *Differ) compareMaps(
|
||||
left map[string]interface{},
|
||||
right map[string]interface{},
|
||||
) (deltas []Delta) {
|
||||
deltas = make([]Delta, 0)
|
||||
|
||||
names := sortedKeys(left) // stabilize delta order
|
||||
for _, name := range names {
|
||||
if rightValue, ok := right[name]; ok {
|
||||
same, delta := differ.compareValues(Name(name), left[name], rightValue)
|
||||
if !same {
|
||||
deltas = append(deltas, delta)
|
||||
}
|
||||
} else {
|
||||
deltas = append(deltas, NewDeleted(Name(name), left[name]))
|
||||
}
|
||||
}
|
||||
|
||||
names = sortedKeys(right) // stabilize delta order
|
||||
for _, name := range names {
|
||||
if _, ok := left[name]; !ok {
|
||||
deltas = append(deltas, NewAdded(Name(name), right[name]))
|
||||
}
|
||||
}
|
||||
|
||||
return deltas
|
||||
}
|
||||
|
||||
// ApplyPatch applies a Diff to an JSON object. This method is destructive.
|
||||
func (differ *Differ) ApplyPatch(json map[string]interface{}, patch Diff) {
|
||||
applyDeltas(patch.Deltas(), json)
|
||||
}
|
||||
|
||||
type maybe struct {
|
||||
index int
|
||||
lcsIndex int
|
||||
item interface{}
|
||||
}
|
||||
|
||||
func (differ *Differ) compareArrays(
|
||||
left []interface{},
|
||||
right []interface{},
|
||||
) (deltas []Delta) {
|
||||
deltas = make([]Delta, 0)
|
||||
// LCS index pairs
|
||||
lcsPairs := lcs.New(left, right).IndexPairs()
|
||||
|
||||
// list up items not in LCS, they are maybe deleted
|
||||
maybeDeleted := list.New() // but maybe moved or modified
|
||||
lcsI := 0
|
||||
for i, leftValue := range left {
|
||||
if lcsI < len(lcsPairs) && lcsPairs[lcsI].Left == i {
|
||||
lcsI++
|
||||
} else {
|
||||
maybeDeleted.PushBack(maybe{index: i, lcsIndex: lcsI, item: leftValue})
|
||||
}
|
||||
}
|
||||
|
||||
// list up items not in LCS, they are maybe Added
|
||||
maybeAdded := list.New() // but maybe moved or modified
|
||||
lcsI = 0
|
||||
for i, rightValue := range right {
|
||||
if lcsI < len(lcsPairs) && lcsPairs[lcsI].Right == i {
|
||||
lcsI++
|
||||
} else {
|
||||
maybeAdded.PushBack(maybe{index: i, lcsIndex: lcsI, item: rightValue})
|
||||
}
|
||||
}
|
||||
|
||||
// find moved items
|
||||
var delNext *list.Element // for prefetch to remove item in iteration
|
||||
for delCandidate := maybeDeleted.Front(); delCandidate != nil; delCandidate = delNext {
|
||||
delCan := delCandidate.Value.(maybe)
|
||||
delNext = delCandidate.Next()
|
||||
|
||||
for addCandidate := maybeAdded.Front(); addCandidate != nil; addCandidate = addCandidate.Next() {
|
||||
addCan := addCandidate.Value.(maybe)
|
||||
if reflect.DeepEqual(delCan.item, addCan.item) {
|
||||
deltas = append(deltas, NewMoved(Index(delCan.index), Index(addCan.index), delCan.item, nil))
|
||||
maybeAdded.Remove(addCandidate)
|
||||
maybeDeleted.Remove(delCandidate)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find modified or add+del
|
||||
prevIndexDel := 0
|
||||
prevIndexAdd := 0
|
||||
delElement := maybeDeleted.Front()
|
||||
addElement := maybeAdded.Front()
|
||||
for i := 0; i <= len(lcsPairs); i++ { // not "< len(lcsPairs)"
|
||||
var lcsPair lcs.IndexPair
|
||||
var delSize, addSize int
|
||||
if i < len(lcsPairs) {
|
||||
lcsPair = lcsPairs[i]
|
||||
delSize = lcsPair.Left - prevIndexDel - 1
|
||||
addSize = lcsPair.Right - prevIndexAdd - 1
|
||||
prevIndexDel = lcsPair.Left
|
||||
prevIndexAdd = lcsPair.Right
|
||||
}
|
||||
|
||||
var delSlice []maybe
|
||||
if delSize > 0 {
|
||||
delSlice = make([]maybe, 0, delSize)
|
||||
} else {
|
||||
delSlice = make([]maybe, 0, maybeDeleted.Len())
|
||||
}
|
||||
for ; delElement != nil; delElement = delElement.Next() {
|
||||
d := delElement.Value.(maybe)
|
||||
if d.lcsIndex != i {
|
||||
break
|
||||
}
|
||||
delSlice = append(delSlice, d)
|
||||
}
|
||||
|
||||
var addSlice []maybe
|
||||
if addSize > 0 {
|
||||
addSlice = make([]maybe, 0, addSize)
|
||||
} else {
|
||||
addSlice = make([]maybe, 0, maybeAdded.Len())
|
||||
}
|
||||
for ; addElement != nil; addElement = addElement.Next() {
|
||||
a := addElement.Value.(maybe)
|
||||
if a.lcsIndex != i {
|
||||
break
|
||||
}
|
||||
addSlice = append(addSlice, a)
|
||||
}
|
||||
|
||||
if len(delSlice) > 0 && len(addSlice) > 0 {
|
||||
var bestDeltas []Delta
|
||||
bestDeltas, delSlice, addSlice = differ.maximizeSimilarities(delSlice, addSlice)
|
||||
for _, delta := range bestDeltas {
|
||||
deltas = append(deltas, delta)
|
||||
}
|
||||
}
|
||||
|
||||
for _, del := range delSlice {
|
||||
deltas = append(deltas, NewDeleted(Index(del.index), del.item))
|
||||
}
|
||||
for _, add := range addSlice {
|
||||
deltas = append(deltas, NewAdded(Index(add.index), add.item))
|
||||
}
|
||||
}
|
||||
|
||||
return deltas
|
||||
}
|
||||
|
||||
func (differ *Differ) compareValues(
|
||||
position Position,
|
||||
left interface{},
|
||||
right interface{},
|
||||
) (same bool, delta Delta) {
|
||||
if reflect.TypeOf(left) != reflect.TypeOf(right) {
|
||||
return false, NewModified(position, left, right)
|
||||
}
|
||||
|
||||
switch left.(type) {
|
||||
|
||||
case map[string]interface{}:
|
||||
l := left.(map[string]interface{})
|
||||
childDeltas := differ.compareMaps(l, right.(map[string]interface{}))
|
||||
if len(childDeltas) > 0 {
|
||||
return false, NewObject(position, childDeltas)
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
l := left.([]interface{})
|
||||
childDeltas := differ.compareArrays(l, right.([]interface{}))
|
||||
|
||||
if len(childDeltas) > 0 {
|
||||
return false, NewArray(position, childDeltas)
|
||||
}
|
||||
|
||||
default:
|
||||
if !reflect.DeepEqual(left, right) {
|
||||
|
||||
if reflect.ValueOf(left).Kind() == reflect.String &&
|
||||
reflect.ValueOf(right).Kind() == reflect.String &&
|
||||
differ.textDiffMinimumLength <= len(left.(string)) {
|
||||
|
||||
textDiff := dmp.New()
|
||||
patchs := textDiff.PatchMake(left.(string), right.(string))
|
||||
return false, NewTextDiff(position, patchs, left, right)
|
||||
|
||||
} else {
|
||||
return false, NewModified(position, left, right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func applyDeltas(deltas []Delta, object interface{}) interface{} {
|
||||
preDeltas := make(preDeltas, 0)
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case PreDelta:
|
||||
preDeltas = append(preDeltas, delta.(PreDelta))
|
||||
}
|
||||
}
|
||||
sort.Sort(preDeltas)
|
||||
for _, delta := range preDeltas {
|
||||
object = delta.PreApply(object)
|
||||
}
|
||||
|
||||
postDeltas := make(postDeltas, 0, len(deltas)-len(preDeltas))
|
||||
for _, delta := range deltas {
|
||||
switch delta.(type) {
|
||||
case PostDelta:
|
||||
postDeltas = append(postDeltas, delta.(PostDelta))
|
||||
}
|
||||
}
|
||||
sort.Sort(postDeltas)
|
||||
|
||||
for _, delta := range postDeltas {
|
||||
object = delta.PostApply(object)
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
func (differ *Differ) maximizeSimilarities(left []maybe, right []maybe) (resultDeltas []Delta, freeLeft, freeRight []maybe) {
|
||||
deltaTable := make([][]Delta, len(left))
|
||||
for i := 0; i < len(left); i++ {
|
||||
deltaTable[i] = make([]Delta, len(right))
|
||||
}
|
||||
for i, leftValue := range left {
|
||||
for j, rightValue := range right {
|
||||
_, delta := differ.compareValues(Index(rightValue.index), leftValue.item, rightValue.item)
|
||||
deltaTable[i][j] = delta
|
||||
}
|
||||
}
|
||||
|
||||
sizeX := len(left) + 1 // margins for both sides
|
||||
sizeY := len(right) + 1
|
||||
|
||||
// fill out with similarities
|
||||
dpTable := make([][]float64, sizeX)
|
||||
for i := 0; i < sizeX; i++ {
|
||||
dpTable[i] = make([]float64, sizeY)
|
||||
}
|
||||
for x := sizeX - 2; x >= 0; x-- {
|
||||
for y := sizeY - 2; y >= 0; y-- {
|
||||
prevX := dpTable[x+1][y]
|
||||
prevY := dpTable[x][y+1]
|
||||
score := deltaTable[x][y].Similarity() + dpTable[x+1][y+1]
|
||||
|
||||
dpTable[x][y] = max(prevX, prevY, score)
|
||||
}
|
||||
}
|
||||
|
||||
minLength := len(left)
|
||||
if minLength > len(right) {
|
||||
minLength = len(right)
|
||||
}
|
||||
maxInvalidLength := minLength - 1
|
||||
|
||||
freeLeft = make([]maybe, 0, len(left)-minLength)
|
||||
freeRight = make([]maybe, 0, len(right)-minLength)
|
||||
|
||||
resultDeltas = make([]Delta, 0, minLength)
|
||||
var x, y int
|
||||
for x, y = 0, 0; x <= sizeX-2 && y <= sizeY-2; {
|
||||
current := dpTable[x][y]
|
||||
nextX := dpTable[x+1][y]
|
||||
nextY := dpTable[x][y+1]
|
||||
|
||||
xValidLength := len(left) - maxInvalidLength + y
|
||||
yValidLength := len(right) - maxInvalidLength + x
|
||||
|
||||
if x+1 < xValidLength && current == nextX {
|
||||
freeLeft = append(freeLeft, left[x])
|
||||
x++
|
||||
} else if y+1 < yValidLength && current == nextY {
|
||||
freeRight = append(freeRight, right[y])
|
||||
y++
|
||||
} else {
|
||||
resultDeltas = append(resultDeltas, deltaTable[x][y])
|
||||
x++
|
||||
y++
|
||||
}
|
||||
}
|
||||
for ; x < sizeX-1; x++ {
|
||||
freeLeft = append(freeLeft, left[x-1])
|
||||
}
|
||||
for ; y < sizeY-1; y++ {
|
||||
freeRight = append(freeRight, right[y-1])
|
||||
}
|
||||
|
||||
return resultDeltas, freeLeft, freeRight
|
||||
}
|
||||
|
||||
func deltasSimilarity(deltas []Delta) (similarity float64) {
|
||||
for _, delta := range deltas {
|
||||
similarity += delta.Similarity()
|
||||
}
|
||||
similarity = similarity / float64(len(deltas))
|
||||
return
|
||||
}
|
||||
|
||||
func stringSimilarity(left, right string) (similarity float64) {
|
||||
matchingLength := float64(
|
||||
lcs.New(
|
||||
stringToInterfaceSlice(left),
|
||||
stringToInterfaceSlice(right),
|
||||
).Length(),
|
||||
)
|
||||
similarity =
|
||||
(matchingLength / float64(len(left))) * (matchingLength / float64(len(right)))
|
||||
return
|
||||
}
|
||||
|
||||
func stringToInterfaceSlice(str string) []interface{} {
|
||||
s := make([]interface{}, len(str))
|
||||
for i, v := range str {
|
||||
s[i] = v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func sortedKeys(m map[string]interface{}) (keys []string) {
|
||||
keys = make([]string, 0, len(m))
|
||||
for key, _ := range m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return
|
||||
}
|
||||
|
||||
func max(first float64, rest ...float64) (max float64) {
|
||||
max = first
|
||||
for _, value := range rest {
|
||||
if max < value {
|
||||
max = value
|
||||
}
|
||||
}
|
||||
return max
|
||||
}
|
||||
131
vendor/github.com/yudai/gojsondiff/unmarshaler.go
generated
vendored
Normal file
131
vendor/github.com/yudai/gojsondiff/unmarshaler.go
generated
vendored
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package gojsondiff
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
dmp "github.com/sergi/go-diff/diffmatchpatch"
|
||||
"io"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Unmarshaller struct {
|
||||
}
|
||||
|
||||
func NewUnmarshaller() *Unmarshaller {
|
||||
return &Unmarshaller{}
|
||||
}
|
||||
|
||||
func (um *Unmarshaller) UnmarshalBytes(diffBytes []byte) (Diff, error) {
|
||||
var diffObj map[string]interface{}
|
||||
json.Unmarshal(diffBytes, &diffObj)
|
||||
return um.UnmarshalObject(diffObj)
|
||||
}
|
||||
|
||||
func (um *Unmarshaller) UnmarshalString(diffString string) (Diff, error) {
|
||||
return um.UnmarshalBytes([]byte(diffString))
|
||||
}
|
||||
|
||||
func (um *Unmarshaller) UnmarshalReader(diffReader io.Reader) (Diff, error) {
|
||||
var diffBytes []byte
|
||||
io.ReadFull(diffReader, diffBytes)
|
||||
return um.UnmarshalBytes(diffBytes)
|
||||
}
|
||||
|
||||
func (um *Unmarshaller) UnmarshalObject(diffObj map[string]interface{}) (Diff, error) {
|
||||
result, err := process(Name(""), diffObj)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &diff{deltas: result.(*Object).Deltas}, nil
|
||||
}
|
||||
|
||||
func process(position Position, object interface{}) (Delta, error) {
|
||||
var delta Delta
|
||||
switch object.(type) {
|
||||
case map[string]interface{}:
|
||||
o := object.(map[string]interface{})
|
||||
if isArray, typed := o["_t"]; typed && isArray == "a" {
|
||||
deltas := make([]Delta, 0, len(o))
|
||||
for name, value := range o {
|
||||
if name == "_t" {
|
||||
continue
|
||||
}
|
||||
|
||||
normalizedName := name
|
||||
if normalizedName[0] == '_' {
|
||||
normalizedName = name[1:]
|
||||
}
|
||||
index, err := strconv.Atoi(normalizedName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
childDelta, err := process(Index(index), value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
deltas = append(deltas, childDelta)
|
||||
}
|
||||
|
||||
for _, d := range deltas {
|
||||
switch d.(type) {
|
||||
case *Moved:
|
||||
moved := d.(*Moved)
|
||||
|
||||
var dd interface{}
|
||||
var i int
|
||||
for i, dd = range deltas {
|
||||
switch dd.(type) {
|
||||
case *Moved:
|
||||
case PostDelta:
|
||||
pd := dd.(PostDelta)
|
||||
if moved.PostPosition() == pd.PostPosition() {
|
||||
moved.Delta = pd
|
||||
deltas = append(deltas[:i], deltas[i+1:]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delta = NewArray(position, deltas)
|
||||
} else {
|
||||
deltas := make([]Delta, 0, len(o))
|
||||
for name, value := range o {
|
||||
childDelta, err := process(Name(name), value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
deltas = append(deltas, childDelta)
|
||||
}
|
||||
delta = NewObject(position, deltas)
|
||||
}
|
||||
case []interface{}:
|
||||
o := object.([]interface{})
|
||||
switch len(o) {
|
||||
case 1:
|
||||
delta = NewAdded(position, o[0])
|
||||
case 2:
|
||||
delta = NewModified(position, o[0], o[1])
|
||||
case 3:
|
||||
switch o[2] {
|
||||
case float64(0):
|
||||
delta = NewDeleted(position, o[0])
|
||||
case float64(2):
|
||||
dmp := dmp.New()
|
||||
patches, err := dmp.PatchFromText(o[0].(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delta = NewTextDiff(position, patches, nil, nil)
|
||||
case float64(3):
|
||||
delta = NewMoved(position, Index(int(o[1].(float64))), nil, nil)
|
||||
default:
|
||||
return nil, errors.New("Unknown delta type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return delta, nil
|
||||
}
|
||||
11
vendor/github.com/yudai/gojsondiff/wercker.yml
generated
vendored
Normal file
11
vendor/github.com/yudai/gojsondiff/wercker.yml
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
box: golang:1.6.3
|
||||
|
||||
build:
|
||||
steps:
|
||||
- setup-go-workspace
|
||||
- script:
|
||||
name: tools
|
||||
code: make tools
|
||||
- script:
|
||||
name: test
|
||||
code: make test
|
||||
21
vendor/github.com/yudai/golcs/LICENSE
generated
vendored
Normal file
21
vendor/github.com/yudai/golcs/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Iwasaki Yudai
|
||||
|
||||
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.
|
||||
60
vendor/github.com/yudai/golcs/README.md
generated
vendored
Normal file
60
vendor/github.com/yudai/golcs/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Go Longest Common Subsequence (LCS)
|
||||
|
||||
[][godoc]
|
||||
[][license]
|
||||
|
||||
[godoc]: https://godoc.org/github.com/yudai/golcs
|
||||
[license]: https://github.com/yudai/golcs/blob/master/LICENSE
|
||||
|
||||
A package to calculate [LCS](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem) of slices.
|
||||
|
||||
## Usage
|
||||
|
||||
```sh
|
||||
go get github.com/yudai/golcs
|
||||
```
|
||||
|
||||
```go
|
||||
import " github.com/yudai/golcs"
|
||||
|
||||
left = []interface{}{1, 2, 5, 3, 1, 1, 5, 8, 3}
|
||||
right = []interface{}{1, 2, 3, 3, 4, 4, 5, 1, 6}
|
||||
|
||||
lcs := golcs.New(left, right)
|
||||
|
||||
lcs.Values() // LCS values => []interface{}{1, 2, 5, 1}
|
||||
lcs.IndexPairs() // Matched indices => [{Left: 0, Right: 0}, {Left: 1, Right: 1}, {Left: 2, Right: 6}, {Left: 4, Right: 7}]
|
||||
lcs.Length() // Matched length => 4
|
||||
|
||||
lcs.Table() // Memo table
|
||||
```
|
||||
|
||||
All the methods of `Lcs` cache their return values. For example, the memo table is calculated only once and reused when `Values()`, `Length()` and other methods are called.
|
||||
|
||||
|
||||
## FAQ
|
||||
|
||||
### How can I give `[]byte` values to `Lcs()` as its arguments?
|
||||
|
||||
As `[]interface{}` is incompatible with `[]othertype` like `[]byte`, you need to create a `[]interface{}` slice and copy the values in your `[]byte` slice into it. Unfortunately, Go doesn't provide any mesure to cast a slice into `[]interface{}` with zero cost. Your copy costs O(n).
|
||||
|
||||
```go
|
||||
leftBytes := []byte("TGAGTA")
|
||||
left = make([]interface{}, len(leftBytes))
|
||||
for i, v := range leftBytes {
|
||||
left[i] = v
|
||||
}
|
||||
|
||||
rightBytes := []byte("GATA")
|
||||
right = make([]interface{}, len(rightBytes))
|
||||
for i, v := range rightBytes {
|
||||
right[i] = v
|
||||
}
|
||||
|
||||
lcs.New(left, right)
|
||||
```
|
||||
|
||||
|
||||
## LICENSE
|
||||
|
||||
The MIT license (See `LICENSE` for detail)
|
||||
195
vendor/github.com/yudai/golcs/golcs.go
generated
vendored
Normal file
195
vendor/github.com/yudai/golcs/golcs.go
generated
vendored
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
// package lcs provides functions to calculate Longest Common Subsequence (LCS)
|
||||
// values from two arbitrary arrays.
|
||||
package lcs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Lcs is the interface to calculate the LCS of two arrays.
|
||||
type Lcs interface {
|
||||
// Values calculates the LCS value of the two arrays.
|
||||
Values() (values []interface{})
|
||||
// ValueContext is a context aware version of Values()
|
||||
ValuesContext(ctx context.Context) (values []interface{}, err error)
|
||||
// IndexPairs calculates paris of indices which have the same value in LCS.
|
||||
IndexPairs() (pairs []IndexPair)
|
||||
// IndexPairsContext is a context aware version of IndexPairs()
|
||||
IndexPairsContext(ctx context.Context) (pairs []IndexPair, err error)
|
||||
// Length calculates the length of the LCS.
|
||||
Length() (length int)
|
||||
// LengthContext is a context aware version of Length()
|
||||
LengthContext(ctx context.Context) (length int, err error)
|
||||
// Left returns one of the two arrays to be compared.
|
||||
Left() (leftValues []interface{})
|
||||
// Right returns the other of the two arrays to be compared.
|
||||
Right() (righttValues []interface{})
|
||||
}
|
||||
|
||||
// IndexPair represents an pair of indeices in the Left and Right arrays found in the LCS value.
|
||||
type IndexPair struct {
|
||||
Left int
|
||||
Right int
|
||||
}
|
||||
|
||||
type lcs struct {
|
||||
left []interface{}
|
||||
right []interface{}
|
||||
/* for caching */
|
||||
table [][]int
|
||||
indexPairs []IndexPair
|
||||
values []interface{}
|
||||
}
|
||||
|
||||
// New creates a new LCS calculator from two arrays.
|
||||
func New(left, right []interface{}) Lcs {
|
||||
return &lcs{
|
||||
left: left,
|
||||
right: right,
|
||||
table: nil,
|
||||
indexPairs: nil,
|
||||
values: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Table implements Lcs.Table()
|
||||
func (lcs *lcs) Table() (table [][]int) {
|
||||
table, _ = lcs.TableContext(context.Background())
|
||||
return table
|
||||
}
|
||||
|
||||
// Table implements Lcs.TableContext()
|
||||
func (lcs *lcs) TableContext(ctx context.Context) (table [][]int, err error) {
|
||||
if lcs.table != nil {
|
||||
return lcs.table, nil
|
||||
}
|
||||
|
||||
sizeX := len(lcs.left) + 1
|
||||
sizeY := len(lcs.right) + 1
|
||||
|
||||
table = make([][]int, sizeX)
|
||||
for x := 0; x < sizeX; x++ {
|
||||
table[x] = make([]int, sizeY)
|
||||
}
|
||||
|
||||
for y := 1; y < sizeY; y++ {
|
||||
select { // check in each y to save some time
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
// nop
|
||||
}
|
||||
for x := 1; x < sizeX; x++ {
|
||||
increment := 0
|
||||
if reflect.DeepEqual(lcs.left[x-1], lcs.right[y-1]) {
|
||||
increment = 1
|
||||
}
|
||||
table[x][y] = max(table[x-1][y-1]+increment, table[x-1][y], table[x][y-1])
|
||||
}
|
||||
}
|
||||
|
||||
lcs.table = table
|
||||
return table, nil
|
||||
}
|
||||
|
||||
// Table implements Lcs.Length()
|
||||
func (lcs *lcs) Length() (length int) {
|
||||
length, _ = lcs.LengthContext(context.Background())
|
||||
return length
|
||||
}
|
||||
|
||||
// Table implements Lcs.LengthContext()
|
||||
func (lcs *lcs) LengthContext(ctx context.Context) (length int, err error) {
|
||||
table, err := lcs.TableContext(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return table[len(lcs.left)][len(lcs.right)], nil
|
||||
}
|
||||
|
||||
// Table implements Lcs.IndexPairs()
|
||||
func (lcs *lcs) IndexPairs() (pairs []IndexPair) {
|
||||
pairs, _ = lcs.IndexPairsContext(context.Background())
|
||||
return pairs
|
||||
}
|
||||
|
||||
// Table implements Lcs.IndexPairsContext()
|
||||
func (lcs *lcs) IndexPairsContext(ctx context.Context) (pairs []IndexPair, err error) {
|
||||
if lcs.indexPairs != nil {
|
||||
return lcs.indexPairs, nil
|
||||
}
|
||||
|
||||
table, err := lcs.TableContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pairs = make([]IndexPair, table[len(table)-1][len(table[0])-1])
|
||||
|
||||
for x, y := len(lcs.left), len(lcs.right); x > 0 && y > 0; {
|
||||
if reflect.DeepEqual(lcs.left[x-1], lcs.right[y-1]) {
|
||||
pairs[table[x][y]-1] = IndexPair{Left: x - 1, Right: y - 1}
|
||||
x--
|
||||
y--
|
||||
} else {
|
||||
if table[x-1][y] >= table[x][y-1] {
|
||||
x--
|
||||
} else {
|
||||
y--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lcs.indexPairs = pairs
|
||||
|
||||
return pairs, nil
|
||||
}
|
||||
|
||||
// Table implements Lcs.Values()
|
||||
func (lcs *lcs) Values() (values []interface{}) {
|
||||
values, _ = lcs.ValuesContext(context.Background())
|
||||
return values
|
||||
}
|
||||
|
||||
// Table implements Lcs.ValuesContext()
|
||||
func (lcs *lcs) ValuesContext(ctx context.Context) (values []interface{}, err error) {
|
||||
if lcs.values != nil {
|
||||
return lcs.values, nil
|
||||
}
|
||||
|
||||
pairs, err := lcs.IndexPairsContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
values = make([]interface{}, len(pairs))
|
||||
for i, pair := range pairs {
|
||||
values[i] = lcs.left[pair.Left]
|
||||
}
|
||||
lcs.values = values
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// Table implements Lcs.Left()
|
||||
func (lcs *lcs) Left() (leftValues []interface{}) {
|
||||
leftValues = lcs.left
|
||||
return
|
||||
}
|
||||
|
||||
// Table implements Lcs.Right()
|
||||
func (lcs *lcs) Right() (rightValues []interface{}) {
|
||||
rightValues = lcs.right
|
||||
return
|
||||
}
|
||||
|
||||
func max(first int, rest ...int) (max int) {
|
||||
max = first
|
||||
for _, value := range rest {
|
||||
if value > max {
|
||||
max = value
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue