JUCE-7.0.12-0-g4f43011b96 JUCE-7.0.12-0-g4f43011b96
JUCE — C++ application framework with suport for VST, VST3, LV2 audio plug-ins

« « « Anklang Documentation
Loading...
Searching...
No Matches
moduleinfoparser.cpp
Go to the documentation of this file.
1 //-----------------------------------------------------------------------------
2// Project : VST SDK
3// Flags : clang-format SMTGSequencer
4//
5// Category : moduleinfo
6// Filename : public.sdk/source/vst/moduleinfo/moduleinfoparser.cpp
7// Created by : Steinberg, 01/2022
8// Description : utility functions to parse moduleinfo json files
9//
10//-----------------------------------------------------------------------------
11// LICENSE
12// (c) 2023, Steinberg Media Technologies GmbH, All Rights Reserved
13//-----------------------------------------------------------------------------
14// Redistribution and use in source and binary forms, with or without modification,
15// are permitted provided that the following conditions are met:
16//
17// * Redistributions of source code must retain the above copyright notice,
18// this list of conditions and the following disclaimer.
19// * Redistributions in binary form must reproduce the above copyright notice,
20// this list of conditions and the following disclaimer in the documentation
21// and/or other materials provided with the distribution.
22// * Neither the name of the Steinberg Media Technologies nor the names of its
23// contributors may be used to endorse or promote products derived from this
24// software without specific prior written permission.
25//
26// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
34// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35// OF THE POSSIBILITY OF SUCH DAMAGE.
36//-----------------------------------------------------------------------------
37
38#include "moduleinfoparser.h"
39#include "jsoncxx.h"
41#include <limits>
42#include <stdexcept>
43
44//------------------------------------------------------------------------
45namespace Steinberg::ModuleInfoLib {
46namespace {
47
48//------------------------------------------------------------------------
49void printJsonParseError (json_parse_result_s& parseResult, std::ostream& errorOut)
50{
51 errorOut << "error : "
52 << JSON::errorToString (static_cast<json_parse_error_e> (parseResult.error)) << '\n';
53 errorOut << "offset : " << parseResult.error_offset << '\n';
54 errorOut << "line no: " << parseResult.error_line_no << '\n';
55 errorOut << "row no : " << parseResult.error_row_no << '\n';
56}
57
58//------------------------------------------------------------------------
59struct parse_error : std::exception
60{
61 parse_error (const std::string& str, const JSON::Value& value)
62 : str (str), location (value.getSourceLocation ())
63 {
64 addLocation (location);
65 }
66 parse_error (const std::string& str, const JSON::String& value)
67 : str (str), location (value.getSourceLocation ())
68 {
69 addLocation (location);
70 }
71 const char* what () const noexcept override { return str.data (); }
72
73private:
74 void addLocation (const JSON::SourceLocation& loc)
75 {
76 str += '\n';
77 str += "offset:";
78 str += std::to_string (loc.offset);
79 str += '\n';
80 str += "line:";
81 str += std::to_string (loc.line);
82 str += '\n';
83 str += "row:";
84 str += std::to_string (loc.row);
85 str += '\n';
86 }
87
88 std::string str;
89 JSON::SourceLocation location;
90};
91
92//------------------------------------------------------------------------
93struct ModuleInfoJsonParser
94{
95 ModuleInfoJsonParser () = default;
96
97 std::string_view getText (const JSON::Value& value) const
98 {
99 if (auto str = value.asString ())
100 return str->text ();
101 throw parse_error ("Expect a String here", value);
102 }
103
104 template <typename T>
105 T getInteger (const JSON::Value& value) const
106 {
107 if (auto number = value.asNumber ())
108 {
109 if (auto result = number->getInteger ())
110 {
111 if (result > static_cast<int64_t> (std::numeric_limits<T>::max ()) ||
112 result < static_cast<int64_t> (std::numeric_limits<T>::min ()))
113 throw parse_error ("Value is out of range here", value);
114 return static_cast<T> (*result);
115 }
116 throw parse_error ("Expect an Integer here", value);
117 }
118 throw parse_error ("Expect a Number here", value);
119 }
120
121 double getDouble (const JSON::Value& value) const
122 {
123 if (auto number = value.asNumber ())
124 {
125 if (auto result = number->getDouble ())
126 return *result;
127 throw parse_error ("Expect a Double here", value);
128 }
129 throw parse_error ("Expect a Number here", value);
130 }
131
132 void parseFactoryInfo (const JSON::Value& value)
133 {
134 enum ParsedBits
135 {
136 Vendor = 1 << 0,
137 URL = 1 << 1,
138 EMail = 1 << 2,
139 Flags = 1 << 3,
140 };
141 uint32_t parsed {0};
142 if (auto obj = value.asObject ())
143 {
144 for (const auto& el : *obj)
145 {
146 auto elementName = el.name ().text ();
147 if (elementName == "Vendor")
148 {
149 if (parsed & ParsedBits::Vendor)
150 throw parse_error ("Only one 'Vendor' key allowed", el.name ());
151 parsed |= ParsedBits::Vendor;
152 info.factoryInfo.vendor = getText (el.value ());
153 }
154 else if (elementName == "URL")
155 {
156 if (parsed & ParsedBits::URL)
157 throw parse_error ("Only one 'URL' key allowed", el.name ());
158 parsed |= ParsedBits::URL;
159 info.factoryInfo.url = getText (el.value ());
160 }
161 else if (elementName == "E-Mail")
162 {
163 if (parsed & ParsedBits::EMail)
164 throw parse_error ("Only one 'E-Mail' key allowed", el.name ());
165 parsed |= ParsedBits::EMail;
166 info.factoryInfo.email = getText (el.value ());
167 }
168 else if (elementName == "Flags")
169 {
170 if (parsed & ParsedBits::Flags)
171 throw parse_error ("Only one 'Flags' key allowed", el.name ());
172 auto flags = el.value ().asObject ();
173 if (!flags)
174 throw parse_error ("Expect 'Flags' to be a JSON Object", el.name ());
175 for (const auto& flag : *flags)
176 {
177 auto flagName = flag.name ().text ();
178 auto flagValue = flag.value ().asBoolean ();
179 if (!flagValue)
180 throw parse_error ("Flag must be a boolean", flag.value ());
181 if (flagName == "Classes Discardable")
182 {
183 if (*flagValue)
184 info.factoryInfo.flags |= PFactoryInfo::kClassesDiscardable;
185 }
186 else if (flagName == "Component Non Discardable")
187 {
188 if (*flagValue)
189 info.factoryInfo.flags |= PFactoryInfo::kComponentNonDiscardable;
190 }
191 else if (flagName == "Unicode")
192 {
193 if (*flagValue)
194 info.factoryInfo.flags |= PFactoryInfo::kUnicode;
195 }
196 else
197 throw parse_error ("Unknown flag", flag.name ());
198 }
199 parsed |= ParsedBits::Flags;
200 }
201 }
202 }
203 if (!(parsed & ParsedBits::Vendor))
204 throw std::logic_error ("Missing 'Vendor' in Factory Info");
205 if (!(parsed & ParsedBits::URL))
206 throw std::logic_error ("Missing 'URL' in Factory Info");
207 if (!(parsed & ParsedBits::EMail))
208 throw std::logic_error ("Missing 'EMail' in Factory Info");
209 if (!(parsed & ParsedBits::Flags))
210 throw std::logic_error ("Missing 'Flags' in Factory Info");
211 }
212
213 void parseClasses (const JSON::Value& value)
214 {
215 enum ParsedBits
216 {
217 CID = 1 << 0,
218 Category = 1 << 1,
219 Name = 1 << 2,
220 Vendor = 1 << 3,
221 Version = 1 << 4,
222 SDKVersion = 1 << 5,
223 SubCategories = 1 << 6,
224 ClassFlags = 1 << 7,
225 Snapshots = 1 << 8,
226 Cardinality = 1 << 9,
227 };
228
229 auto array = value.asArray ();
230 if (!array)
231 throw parse_error ("Expect Classes Array", value);
232 for (const auto& classInfoEl : *array)
233 {
234 auto classInfo = classInfoEl.value ().asObject ();
235 if (!classInfo)
236 throw parse_error ("Expect Class Object", classInfoEl.value ());
237
238 ModuleInfo::ClassInfo ci {};
239
240 uint32_t parsed {0};
241
242 for (const auto& el : *classInfo)
243 {
244 auto elementName = el.name ().text ();
245 if (elementName == "CID")
246 {
247 if (parsed & ParsedBits::CID)
248 throw parse_error ("Only one 'CID' key allowed", el.name ());
249 ci.cid = getText (el.value ());
250 parsed |= ParsedBits::CID;
251 }
252 else if (elementName == "Category")
253 {
254 if (parsed & ParsedBits::Category)
255 throw parse_error ("Only one 'Category' key allowed", el.name ());
256 ci.category = getText (el.value ());
257 parsed |= ParsedBits::Category;
258 }
259 else if (elementName == "Name")
260 {
261 if (parsed & ParsedBits::Name)
262 throw parse_error ("Only one 'Name' key allowed", el.name ());
263 ci.name = getText (el.value ());
264 parsed |= ParsedBits::Name;
265 }
266 else if (elementName == "Vendor")
267 {
268 if (parsed & ParsedBits::Vendor)
269 throw parse_error ("Only one 'Vendor' key allowed", el.name ());
270 ci.vendor = getText (el.value ());
271 parsed |= ParsedBits::Vendor;
272 }
273 else if (elementName == "Version")
274 {
275 if (parsed & ParsedBits::Version)
276 throw parse_error ("Only one 'Version' key allowed", el.name ());
277 ci.version = getText (el.value ());
278 parsed |= ParsedBits::Version;
279 }
280 else if (elementName == "SDKVersion")
281 {
282 if (parsed & ParsedBits::SDKVersion)
283 throw parse_error ("Only one 'SDKVersion' key allowed", el.name ());
284 ci.sdkVersion = getText (el.value ());
285 parsed |= ParsedBits::SDKVersion;
286 }
287 else if (elementName == "Sub Categories")
288 {
289 if (parsed & ParsedBits::SubCategories)
290 throw parse_error ("Only one 'Sub Categories' key allowed", el.name ());
291 auto subCatArr = el.value ().asArray ();
292 if (!subCatArr)
293 throw parse_error ("Expect Array here", el.value ());
294 for (const auto& catEl : *subCatArr)
295 {
296 auto cat = getText (catEl.value ());
297 ci.subCategories.emplace_back (cat);
298 }
299 parsed |= ParsedBits::SubCategories;
300 }
301 else if (elementName == "Class Flags")
302 {
303 if (parsed & ParsedBits::ClassFlags)
304 throw parse_error ("Only one 'Class Flags' key allowed", el.name ());
305 ci.flags = getInteger<uint32_t> (el.value ());
306 parsed |= ParsedBits::ClassFlags;
307 }
308 else if (elementName == "Cardinality")
309 {
310 if (parsed & ParsedBits::Cardinality)
311 throw parse_error ("Only one 'Cardinality' key allowed", el.name ());
312 ci.cardinality = getInteger<int32_t> (el.value ());
313 parsed |= ParsedBits::Cardinality;
314 }
315 else if (elementName == "Snapshots")
316 {
317 if (parsed & ParsedBits::Snapshots)
318 throw parse_error ("Only one 'Snapshots' key allowed", el.name ());
319 auto snapArr = el.value ().asArray ();
320 if (!snapArr)
321 throw parse_error ("Expect Array here", el.value ());
322 for (const auto& snapEl : *snapArr)
323 {
324 auto snap = snapEl.value ().asObject ();
325 if (!snap)
326 throw parse_error ("Expect Object here", snapEl.value ());
327 ModuleInfo::Snapshot snapshot;
328 for (const auto& spEl : *snap)
329 {
330 auto spElName = spEl.name ().text ();
331 if (spElName == "Path")
332 snapshot.path = getText (spEl.value ());
333 else if (spElName == "Scale Factor")
334 snapshot.scaleFactor = getDouble (spEl.value ());
335 else
336 throw parse_error ("Unexpected key", spEl.name ());
337 }
338 if (snapshot.scaleFactor == 0. || snapshot.path.empty ())
339 throw parse_error ("Missing Snapshot keys", snapEl.value ());
340 ci.snapshots.emplace_back (std::move (snapshot));
341 }
342 parsed |= ParsedBits::Snapshots;
343 }
344 else
345 throw parse_error ("Unexpected key", el.name ());
346 }
347 if (!(parsed & ParsedBits::CID))
348 throw parse_error ("'CID' key missing", classInfoEl.value ());
349 if (!(parsed & ParsedBits::Category))
350 throw parse_error ("'Category' key missing", classInfoEl.value ());
351 if (!(parsed & ParsedBits::Name))
352 throw parse_error ("'Name' key missing", classInfoEl.value ());
353 if (!(parsed & ParsedBits::Vendor))
354 throw parse_error ("'Vendor' key missing", classInfoEl.value ());
355 if (!(parsed & ParsedBits::Version))
356 throw parse_error ("'Version' key missing", classInfoEl.value ());
357 if (!(parsed & ParsedBits::SDKVersion))
358 throw parse_error ("'SDK Version' key missing", classInfoEl.value ());
359 if (!(parsed & ParsedBits::ClassFlags))
360 throw parse_error ("'Class Flags' key missing", classInfoEl.value ());
361 if (!(parsed & ParsedBits::Cardinality))
362 throw parse_error ("'Cardinality' key missing", classInfoEl.value ());
363 info.classes.emplace_back (std::move (ci));
364 }
365 }
366
367 void parseCompatibility (const JSON::Value& value)
368 {
369 auto arr = value.asArray ();
370 if (!arr)
371 throw parse_error ("Expect Array here", value);
372 for (const auto& el : *arr)
373 {
374 auto obj = el.value ().asObject ();
375 if (!obj)
376 throw parse_error ("Expect Object here", el.value ());
377
378 ModuleInfo::Compatibility compat;
379 for (const auto& objEl : *obj)
380 {
381 auto elementName = objEl.name ().text ();
382 if (elementName == "New")
383 compat.newCID = getText (objEl.value ());
384 else if (elementName == "Old")
385 {
386 auto oldElArr = objEl.value ().asArray ();
387 if (!oldElArr)
388 throw parse_error ("Expect Array here", objEl.value ());
389 for (const auto& old : *oldElArr)
390 {
391 compat.oldCID.emplace_back (getText (old.value ()));
392 }
393 }
394 }
395 if (compat.newCID.empty ())
396 throw parse_error ("Expect New CID here", el.value ());
397 if (compat.oldCID.empty ())
398 throw parse_error ("Expect Old CID here", el.value ());
399 info.compatibility.emplace_back (std::move (compat));
400 }
401 }
402
403 void parse (const JSON::Document& doc)
404 {
405 auto docObj = doc.asObject ();
406 if (!docObj)
407 throw parse_error ("Unexpected", doc);
408
409 enum ParsedBits
410 {
411 Name = 1 << 0,
412 Version = 1 << 1,
413 FactoryInfo = 1 << 2,
414 Compatibility = 1 << 3,
415 Classes = 1 << 4,
416 };
417
418 uint32_t parsed {0};
419 for (const auto& el : *docObj)
420 {
421 auto elementName = el.name ().text ();
422 if (elementName == "Name")
423 {
424 if (parsed & ParsedBits::Name)
425 throw parse_error ("Only one 'Name' key allowed", el.name ());
426 parsed |= ParsedBits::Name;
427 info.name = getText (el.value ());
428 }
429 else if (elementName == "Version")
430 {
431 if (parsed & ParsedBits::Version)
432 throw parse_error ("Only one 'Version' key allowed", el.name ());
433 parsed |= ParsedBits::Version;
434 info.version = getText (el.value ());
435 }
436 else if (elementName == "Factory Info")
437 {
438 if (parsed & ParsedBits::FactoryInfo)
439 throw parse_error ("Only one 'Factory Info' key allowed", el.name ());
440 parseFactoryInfo (el.value ());
441 parsed |= ParsedBits::FactoryInfo;
442 }
443 else if (elementName == "Compatibility")
444 {
445 if (parsed & ParsedBits::Compatibility)
446 throw parse_error ("Only one 'Compatibility' key allowed", el.name ());
447 parseCompatibility (el.value ());
448 parsed |= ParsedBits::Compatibility;
449 }
450 else if (elementName == "Classes")
451 {
452 if (parsed & ParsedBits::Classes)
453 throw parse_error ("Only one 'Classes' key allowed", el.name ());
454 parseClasses (el.value ());
455 parsed |= ParsedBits::Classes;
456 }
457 else
458 {
459 throw parse_error ("Unexpected JSON Token", el.name ());
460 }
461 }
462 if (!(parsed & ParsedBits::Name))
463 throw std::logic_error ("'Name' key missing");
464 if (!(parsed & ParsedBits::Version))
465 throw std::logic_error ("'Version' key missing");
466 if (!(parsed & ParsedBits::FactoryInfo))
467 throw std::logic_error ("'Factory Info' key missing");
468 if (!(parsed & ParsedBits::Classes))
469 throw std::logic_error ("'Classes' key missing");
470 }
471
472 ModuleInfo&& takeInfo () { return std::move (info); }
473
474private:
475 ModuleInfo info;
476};
477
478//------------------------------------------------------------------------
479} // anonymous
480
481//------------------------------------------------------------------------
483{
484 auto docVar = JSON::Document::parse (jsonData);
485 if (auto res = std::get_if<json_parse_result_s> (&docVar))
486 {
487 if (optErrorOutput)
488 printJsonParseError (*res, *optErrorOutput);
489 return {};
490 }
491 auto doc = std::get_if<JSON::Document> (&docVar);
492 assert (doc);
493 try
494 {
495 ModuleInfoJsonParser parser;
496 parser.parse (*doc);
497 return parser.takeInfo ();
498 }
499 catch (std::exception& error)
500 {
501 if (optErrorOutput)
502 *optErrorOutput << error.what () << '\n';
503 return {};
504 }
505 // unreachable
506}
507
508//------------------------------------------------------------------------
510 std::ostream* optErrorOutput)
511{
512 auto docVar = JSON::Document::parse (jsonData);
513 if (auto res = std::get_if<json_parse_result_s> (&docVar))
514 {
515 if (optErrorOutput)
516 printJsonParseError (*res, *optErrorOutput);
517 return {};
518 }
519 auto doc = std::get_if<JSON::Document> (&docVar);
520 assert (doc);
521 try
522 {
523 ModuleInfoJsonParser parser;
524 parser.parseCompatibility (*doc);
525 return parser.takeInfo ().compatibility;
526 }
527 catch (std::exception& error)
528 {
529 if (optErrorOutput)
530 *optErrorOutput << error.what () << '\n';
531 return {};
532 }
533 // unreachable
534}
535
536//------------------------------------------------------------------------
537} // Steinberg::ModuelInfoLib
assert
std::optional< ModuleInfo::CompatibilityList > parseCompatibilityJson(std::string_view jsonData, std::ostream *optErrorOutput)
parse a json formatted string to a ModuleInfo::CompatibilityList
std::optional< ModuleInfo > parseJson(std::string_view jsonData, std::ostream *optErrorOutput)
parse a json formatted string to a ModuleInfo struct
T parse(T... args)
typedef int64_t
@ kUnicode
Components have entirely unicode encoded strings (True for VST 3 plug-ins so far).
Definition ipluginbase.h:83
@ kComponentNonDiscardable
Component will not be unloaded until process exit.
Definition ipluginbase.h:80
@ kClassesDiscardable
The number of exported classes can change each time the Module is loaded.
Definition ipluginbase.h:73
T to_string(T... args)
T what(T... args)