GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/json_document.cc Lines: 103 122 84.4 %
Date: 2019-02-03 02:48:13 Branches: 55 78 70.5 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#include "json_document.h"
6
7
#include <cassert>
8
#include <cstdlib>
9
#include <cstring>
10
11
#include "logging.h"
12
#include "util/pointer.h"
13
#include "util/string.h"
14
15
using namespace std;  // NOLINT
16
17
7
bool ToJsonString(const JsonStringInput &input, std::string *output) {
18
7
  if (!output) {
19
    return false;
20
  }
21
22
7
  output->clear();
23
7
  *output = "{";
24
23
  for (size_t i = 0u; i < input.size(); ++i) {
25
    *output +=
26
16
        std::string("\"") + input[i].first + "\":\"" + input[i].second + "\"";
27
16
    if (i < input.size() - 1) {
28
9
      *output += ',';
29
    }
30
  }
31
7
  *output += std::string("}");
32
33
7
  return true;
34
}
35
36
315
JsonDocument *JsonDocument::Create(const string &text) {
37
315
  UniquePtr<JsonDocument> json(new JsonDocument());
38
315
  bool retval = json->Parse(text);
39
315
  if (!retval) return NULL;
40
282
  return json.Release();
41
}
42
43
14
string JsonDocument::EscapeString(const string &input) {
44
14
  string escaped;
45
14
  escaped.reserve(input.length());
46
47
116
  for (unsigned i = 0, s = input.length(); i < s; ++i) {
48
102
    if (input[i] == '\\') {
49
      escaped.push_back('\\');
50
      escaped.push_back('\\');
51
102
    } else if (input[i] == '"') {
52
2
      escaped.push_back('\\');
53
2
      escaped.push_back('"');
54
    } else {
55
100
      escaped.push_back(input[i]);
56
    }
57
  }
58
14
  return escaped;
59
}
60
61
315
JsonDocument::JsonDocument()
62
315
    : allocator_(kDefaultBlockSize), root_(NULL), raw_text_(NULL) {}
63
64
315
JsonDocument::~JsonDocument() {
65
315
  if (raw_text_) free(raw_text_);
66
315
}
67
68
/**
69
 * Parses a JSON string in text.
70
 *
71
 * @return        true if parsing was successful
72
 */
73
315
bool JsonDocument::Parse(const string &text) {
74
315
  assert(root_ == NULL);
75
76
  // The used JSON library 'vjson' is a destructive parser and therefore
77
  // alters the content of the provided buffer.  The buffer must persist as
78
  // name and string values from JSON nodes just point into it.
79
315
  raw_text_ = strdup(text.c_str());
80
81
315
  char *error_pos = 0;
82
315
  char *error_desc = 0;
83
315
  int error_line = 0;
84
  JSON *root =
85
315
      json_parse(raw_text_, &error_pos, &error_desc, &error_line, &allocator_);
86
87
  // check if the json string was parsed successfully
88
315
  if (!root) {
89
    LogCvmfs(kLogUtility, kLogDebug,
90
             "Failed to parse json string. Error at line %d: %s (%s)",
91
33
             error_line, error_desc, error_pos);
92
33
    return false;
93
  }
94
95
282
  root_ = root;
96
282
  return true;
97
}
98
99
3
string JsonDocument::PrintArray(JSON *first_child, PrintOptions print_options) {
100
3
  string result = "[";
101
3
  if (print_options.with_whitespace) {
102
    result += "\n";
103
    print_options.num_indent += 2;
104
  }
105
3
  JSON *value = first_child;
106
3
  if (value != NULL) {
107
2
    result += PrintValue(value, print_options);
108
2
    value = value->next_sibling;
109
  }
110
16
  while (value != NULL) {
111
10
    result += print_options.with_whitespace ? ",\n" : ",";
112
10
    result += PrintValue(value, print_options);
113
10
    value = value->next_sibling;
114
  }
115
3
  if (print_options.with_whitespace) {
116
    result += "\n";
117
    for (unsigned i = 2; i < print_options.num_indent; ++i)
118
      result.push_back(' ');
119
  }
120
3
  return result + "]";
121
}
122
123
/**
124
 * JSON string in a canonical format:
125
 *   - No whitespaces
126
 *   - Variable names and strings in quotes
127
 *
128
 * Can be used as a canonical representation to sign or encrypt a JSON text.
129
 */
130
3
string JsonDocument::PrintCanonical() {
131
3
  if (!root_) return "";
132
3
  PrintOptions print_options;
133
3
  return PrintObject(root_->first_child, print_options);
134
}
135
136
7
string JsonDocument::PrintObject(JSON *first_child,
137
                                 PrintOptions print_options) {
138
7
  string result = "{";
139
7
  if (print_options.with_whitespace) {
140
    result += "\n";
141
    print_options.num_indent += 2;
142
  }
143
7
  JSON *value = first_child;
144
7
  if (value != NULL) {
145
4
    result += PrintValue(value, print_options);
146
4
    value = value->next_sibling;
147
  }
148
23
  while (value != NULL) {
149
9
    result += print_options.with_whitespace ? ",\n" : ",";
150
9
    result += PrintValue(value, print_options);
151
9
    value = value->next_sibling;
152
  }
153
7
  if (print_options.with_whitespace) {
154
    result += "\n";
155
    for (unsigned i = 2; i < print_options.num_indent; ++i)
156
      result.push_back(' ');
157
  }
158
7
  return result + "}";
159
}
160
161
/**
162
 * JSON string for humans.
163
 */
164
string JsonDocument::PrintPretty() {
165
  if (!root_) return "";
166
  PrintOptions print_options;
167
  print_options.with_whitespace = true;
168
  return PrintObject(root_->first_child, print_options);
169
}
170
171
25
std::string JsonDocument::PrintValue(JSON *value, PrintOptions print_options) {
172
25
  assert(value);
173
174
25
  string result;
175
25
  for (unsigned i = 0; i < print_options.num_indent; ++i) result.push_back(' ');
176
25
  if (value->name) {
177
11
    result += "\"" + EscapeString(value->name) + "\":";
178
11
    if (print_options.with_whitespace) result += " ";
179
  }
180


25
  switch (value->type) {
181
    case JSON_NULL:
182
2
      result += "null";
183
2
      break;
184
    case JSON_OBJECT:
185
4
      result += PrintObject(value->first_child, print_options);
186
4
      break;
187
    case JSON_ARRAY:
188
3
      result += PrintArray(value->first_child, print_options);
189
3
      break;
190
    case JSON_STRING:
191
3
      result += "\"" + EscapeString(value->string_value) + "\"";
192
3
      break;
193
    case JSON_INT:
194
8
      result += StringifyInt(value->int_value);
195
8
      break;
196
    case JSON_FLOAT:
197
2
      result += StringifyDouble(value->float_value);
198
2
      break;
199
    case JSON_BOOL:
200
3
      result += value->int_value ? "true" : "false";
201
3
      break;
202
    default:
203
      abort();
204
  }
205
25
  return result;
206
}
207
208
1007
JSON *JsonDocument::SearchInObject(const JSON *json_object, const string &name,
209
                                   const json_type type) {
210

1007
  if (!json_object || (json_object->type != JSON_OBJECT)) return NULL;
211
212
1005
  JSON *walker = json_object->first_child;
213
3223
  while (walker != NULL) {
214

2037
    if (string(walker->name) == name) {
215
824
      return (walker->type == type) ? walker : NULL;
216
    }
217
1213
    walker = walker->next_sibling;
218
  }
219
181
  return NULL;
220
}