-
-
Notifications
You must be signed in to change notification settings - Fork 550
/
operator_encoder_decoder.go
147 lines (121 loc) · 4.33 KB
/
operator_encoder_decoder.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package yqlib
import (
"bufio"
"bytes"
"container/list"
"regexp"
"strings"
"gopkg.in/yaml.v3"
)
func configureEncoder(format PrinterOutputFormat, indent int) Encoder {
switch format {
case JSONOutputFormat:
return NewJSONEncoder(indent, false, false)
case PropsOutputFormat:
return NewPropertiesEncoder(true)
case CSVOutputFormat:
return NewCsvEncoder(',')
case TSVOutputFormat:
return NewCsvEncoder('\t')
case YamlOutputFormat:
return NewYamlEncoder(indent, false, ConfiguredYamlPreferences)
case XMLOutputFormat:
return NewXMLEncoder(indent, ConfiguredXMLPreferences)
case Base64OutputFormat:
return NewBase64Encoder()
case UriOutputFormat:
return NewUriEncoder()
case ShOutputFormat:
return NewShEncoder()
}
panic("invalid encoder")
}
func encodeToString(candidate *CandidateNode, prefs encoderPreferences) (string, error) {
var output bytes.Buffer
log.Debug("printing with indent: %v", prefs.indent)
encoder := configureEncoder(prefs.format, prefs.indent)
printer := NewPrinter(encoder, NewSinglePrinterWriter(bufio.NewWriter(&output)))
err := printer.PrintResults(candidate.AsList())
return output.String(), err
}
type encoderPreferences struct {
format PrinterOutputFormat
indent int
}
/* encodes object as yaml string */
func encodeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
preferences := expressionNode.Operation.Preferences.(encoderPreferences)
var results = list.New()
hasOnlyOneNewLine := regexp.MustCompile("[^\n].*\n$")
endWithNewLine := regexp.MustCompile(".*\n$")
chomper := regexp.MustCompile("\n+$")
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
stringValue, err := encodeToString(candidate, preferences)
if err != nil {
return Context{}, err
}
// remove trailing newlines if needed.
// check if we originally decoded this path, and the original thing had a single line.
originalList := context.GetVariable("decoded: " + candidate.GetKey())
if originalList != nil && originalList.Len() > 0 && hasOnlyOneNewLine.MatchString(stringValue) {
original := originalList.Front().Value.(*CandidateNode)
originalNode := unwrapDoc(original.Node)
// original block did not have a new line at the end, get rid of this one too
if !endWithNewLine.MatchString(originalNode.Value) {
stringValue = chomper.ReplaceAllString(stringValue, "")
}
}
// dont print a new line when printing json on a single line.
if (preferences.format == JSONOutputFormat && preferences.indent == 0) ||
preferences.format == CSVOutputFormat ||
preferences.format == TSVOutputFormat {
stringValue = chomper.ReplaceAllString(stringValue, "")
}
stringContentNode := &yaml.Node{Kind: yaml.ScalarNode, Tag: "!!str", Value: stringValue}
results.PushBack(candidate.CreateReplacement(stringContentNode))
}
return context.ChildContext(results), nil
}
type decoderPreferences struct {
format InputFormat
}
/* takes a string and decodes it back into an object */
func decodeOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
preferences := expressionNode.Operation.Preferences.(decoderPreferences)
var decoder Decoder
switch preferences.format {
case YamlInputFormat:
decoder = NewYamlDecoder(ConfiguredYamlPreferences)
case XMLInputFormat:
decoder = NewXMLDecoder(ConfiguredXMLPreferences)
case Base64InputFormat:
decoder = NewBase64Decoder()
case PropertiesInputFormat:
decoder = NewPropertiesDecoder()
case CSVObjectInputFormat:
decoder = NewCSVObjectDecoder(',')
case TSVObjectInputFormat:
decoder = NewCSVObjectDecoder('\t')
case UriInputFormat:
decoder = NewUriDecoder()
}
var results = list.New()
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
context.SetVariable("decoded: "+candidate.GetKey(), candidate.AsList())
log.Debugf("got: [%v]", candidate.Node.Value)
err := decoder.Init(strings.NewReader(unwrapDoc(candidate.Node).Value))
if err != nil {
return Context{}, err
}
decodedNode, errorReading := decoder.Decode()
if errorReading != nil {
return Context{}, errorReading
}
//first node is a doc
node := unwrapDoc(decodedNode.Node)
results.PushBack(candidate.CreateReplacement(node))
}
return context.ChildContext(results), nil
}