1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262 |
- // Tencent is pleased to support the open source community by making RapidJSON available->
- //
- // Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
- //
- // Licensed under the MIT License (the "License"); you may not use this file except
- // in compliance with the License-> You may obtain a copy of the License at
- //
- // http://opensource->org/licenses/MIT
- //
- // Unless required by applicable law or agreed to in writing, software distributed
- // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- // CONDITIONS OF ANY KIND, either express or implied-> See the License for the
- // specific language governing permissions and limitations under the License->
- #ifndef RAPIDJSON_SCHEMA_H_
- #define RAPIDJSON_SCHEMA_H_
- #include "document.h"
- #include "pointer.h"
- #include "stringbuffer.h"
- #include "error/en.h"
- #include "uri.h"
- #include <cmath> // abs, floor
- #if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
- #define RAPIDJSON_SCHEMA_USE_INTERNALREGEX 1
- #endif
- #if !defined(RAPIDJSON_SCHEMA_USE_STDREGEX) || !(__cplusplus >=201103L || (defined(_MSC_VER) && _MSC_VER >= 1800))
- #define RAPIDJSON_SCHEMA_USE_STDREGEX 0
- #endif
- #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
- #include "internal/regex.h"
- #elif RAPIDJSON_SCHEMA_USE_STDREGEX
- #include <regex>
- #endif
- #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX || RAPIDJSON_SCHEMA_USE_STDREGEX
- #define RAPIDJSON_SCHEMA_HAS_REGEX 1
- #else
- #define RAPIDJSON_SCHEMA_HAS_REGEX 0
- #endif
- #ifndef RAPIDJSON_SCHEMA_VERBOSE
- #define RAPIDJSON_SCHEMA_VERBOSE 0
- #endif
- RAPIDJSON_DIAG_PUSH
- #if defined(__GNUC__)
- RAPIDJSON_DIAG_OFF(effc++)
- #endif
- #ifdef __clang__
- RAPIDJSON_DIAG_OFF(weak-vtables)
- RAPIDJSON_DIAG_OFF(exit-time-destructors)
- RAPIDJSON_DIAG_OFF(c++98-compat-pedantic)
- RAPIDJSON_DIAG_OFF(variadic-macros)
- #elif defined(_MSC_VER)
- RAPIDJSON_DIAG_OFF(4512) // assignment operator could not be generated
- #endif
- RAPIDJSON_NAMESPACE_BEGIN
- ///////////////////////////////////////////////////////////////////////////////
- // Verbose Utilities
- #if RAPIDJSON_SCHEMA_VERBOSE
- namespace internal {
- inline void PrintInvalidKeywordData(const char* keyword) {
- printf(" Fail keyword: '%s'\n", keyword);
- }
- inline void PrintInvalidKeywordData(const wchar_t* keyword) {
- wprintf(L" Fail keyword: '%ls'\n", keyword);
- }
- inline void PrintInvalidDocumentData(const char* document) {
- printf(" Fail document: '%s'\n", document);
- }
- inline void PrintInvalidDocumentData(const wchar_t* document) {
- wprintf(L" Fail document: '%ls'\n", document);
- }
- inline void PrintValidatorPointersData(const char* s, const char* d, unsigned depth) {
- printf(" Sch: %*s'%s'\n Doc: %*s'%s'\n", depth * 4, " ", s, depth * 4, " ", d);
- }
- inline void PrintValidatorPointersData(const wchar_t* s, const wchar_t* d, unsigned depth) {
- wprintf(L" Sch: %*ls'%ls'\n Doc: %*ls'%ls'\n", depth * 4, L" ", s, depth * 4, L" ", d);
- }
- inline void PrintSchemaIdsData(const char* base, const char* local, const char* resolved) {
- printf(" Resolving id: Base: '%s', Local: '%s', Resolved: '%s'\n", base, local, resolved);
- }
- inline void PrintSchemaIdsData(const wchar_t* base, const wchar_t* local, const wchar_t* resolved) {
- wprintf(L" Resolving id: Base: '%ls', Local: '%ls', Resolved: '%ls'\n", base, local, resolved);
- }
- inline void PrintMethodData(const char* method) {
- printf("%s\n", method);
- }
- inline void PrintMethodData(const char* method, bool b) {
- printf("%s, Data: '%s'\n", method, b ? "true" : "false");
- }
- inline void PrintMethodData(const char* method, int64_t i) {
- printf("%s, Data: '%" PRId64 "'\n", method, i);
- }
- inline void PrintMethodData(const char* method, uint64_t u) {
- printf("%s, Data: '%" PRIu64 "'\n", method, u);
- }
- inline void PrintMethodData(const char* method, double d) {
- printf("%s, Data: '%lf'\n", method, d);
- }
- inline void PrintMethodData(const char* method, const char* s) {
- printf("%s, Data: '%s'\n", method, s);
- }
- inline void PrintMethodData(const char* method, const wchar_t* s) {
- wprintf(L"%hs, Data: '%ls'\n", method, s);
- }
- inline void PrintMethodData(const char* method, const char* s1, const char* s2) {
- printf("%s, Data: '%s', '%s'\n", method, s1, s2);
- }
- inline void PrintMethodData(const char* method, const wchar_t* s1, const wchar_t* s2) {
- wprintf(L"%hs, Data: '%ls', '%ls'\n", method, s1, s2);
- }
- } // namespace internal
- #endif // RAPIDJSON_SCHEMA_VERBOSE
- #ifndef RAPIDJSON_SCHEMA_PRINT
- #if RAPIDJSON_SCHEMA_VERBOSE
- #define RAPIDJSON_SCHEMA_PRINT(name, ...) internal::Print##name##Data(__VA_ARGS__)
- #else
- #define RAPIDJSON_SCHEMA_PRINT(name, ...)
- #endif
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- // RAPIDJSON_INVALID_KEYWORD_RETURN
- #define RAPIDJSON_INVALID_KEYWORD_RETURN(code)\
- RAPIDJSON_MULTILINEMACRO_BEGIN\
- context.invalidCode = code;\
- context.invalidKeyword = SchemaType::GetValidateErrorKeyword(code).GetString();\
- RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, context.invalidKeyword);\
- return false;\
- RAPIDJSON_MULTILINEMACRO_END
- ///////////////////////////////////////////////////////////////////////////////
- // ValidateFlag
- /*! \def RAPIDJSON_VALIDATE_DEFAULT_FLAGS
- \ingroup RAPIDJSON_CONFIG
- \brief User-defined kValidateDefaultFlags definition.
- User can define this as any \c ValidateFlag combinations.
- */
- #ifndef RAPIDJSON_VALIDATE_DEFAULT_FLAGS
- #define RAPIDJSON_VALIDATE_DEFAULT_FLAGS kValidateNoFlags
- #endif
- //! Combination of validate flags
- enum ValidateFlag {
- kValidateNoFlags = 0, //!< No flags are set.
- kValidateContinueOnErrorFlag = 1, //!< Don't stop after first validation error.
- kValidateReadFlag = 2, //!< Validation is for a read semantic.
- kValidateWriteFlag = 4, //!< Validation is for a write semantic.
- kValidateDefaultFlags = RAPIDJSON_VALIDATE_DEFAULT_FLAGS //!< Default validate flags. Can be customized by defining RAPIDJSON_VALIDATE_DEFAULT_FLAGS
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Specification
- enum SchemaDraft {
- kDraftUnknown = -1,
- kDraftNone = 0,
- kDraft03 = 3,
- kDraftMin = 4, //!< Current minimum supported draft
- kDraft04 = 4,
- kDraft05 = 5,
- kDraftMax = 5, //!< Current maximum supported draft
- kDraft06 = 6,
- kDraft07 = 7,
- kDraft2019_09 = 8,
- kDraft2020_12 = 9
- };
- enum OpenApiVersion {
- kVersionUnknown = -1,
- kVersionNone = 0,
- kVersionMin = 2, //!< Current minimum supported version
- kVersion20 = 2,
- kVersion30 = 3,
- kVersionMax = 3, //!< Current maximum supported version
- kVersion31 = 4,
- };
- struct Specification {
- Specification(SchemaDraft d) : draft(d), oapi(kVersionNone) {}
- Specification(OpenApiVersion o) : oapi(o) {
- if (oapi == kVersion20) draft = kDraft04;
- else if (oapi == kVersion30) draft = kDraft05;
- else if (oapi == kVersion31) draft = kDraft2020_12;
- else draft = kDraft04;
- }
- ~Specification() {}
- bool IsSupported() const {
- return ((draft >= kDraftMin && draft <= kDraftMax) && ((oapi == kVersionNone) || (oapi >= kVersionMin && oapi <= kVersionMax)));
- }
- SchemaDraft draft;
- OpenApiVersion oapi;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Forward declarations
- template <typename ValueType, typename Allocator>
- class GenericSchemaDocument;
- namespace internal {
- template <typename SchemaDocumentType>
- class Schema;
- ///////////////////////////////////////////////////////////////////////////////
- // ISchemaValidator
- class ISchemaValidator {
- public:
- virtual ~ISchemaValidator() {}
- virtual bool IsValid() const = 0;
- virtual void SetValidateFlags(unsigned flags) = 0;
- virtual unsigned GetValidateFlags() const = 0;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // ISchemaStateFactory
- template <typename SchemaType>
- class ISchemaStateFactory {
- public:
- virtual ~ISchemaStateFactory() {}
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType&, const bool inheritContinueOnErrors) = 0;
- virtual void DestroySchemaValidator(ISchemaValidator* validator) = 0;
- virtual void* CreateHasher() = 0;
- virtual uint64_t GetHashCode(void* hasher) = 0;
- virtual void DestroryHasher(void* hasher) = 0;
- virtual void* MallocState(size_t size) = 0;
- virtual void FreeState(void* p) = 0;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // IValidationErrorHandler
- template <typename SchemaType>
- class IValidationErrorHandler {
- public:
- typedef typename SchemaType::Ch Ch;
- typedef typename SchemaType::SValue SValue;
- virtual ~IValidationErrorHandler() {}
- virtual void NotMultipleOf(int64_t actual, const SValue& expected) = 0;
- virtual void NotMultipleOf(uint64_t actual, const SValue& expected) = 0;
- virtual void NotMultipleOf(double actual, const SValue& expected) = 0;
- virtual void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) = 0;
- virtual void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
- virtual void AboveMaximum(double actual, const SValue& expected, bool exclusive) = 0;
- virtual void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) = 0;
- virtual void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) = 0;
- virtual void BelowMinimum(double actual, const SValue& expected, bool exclusive) = 0;
- virtual void TooLong(const Ch* str, SizeType length, SizeType expected) = 0;
- virtual void TooShort(const Ch* str, SizeType length, SizeType expected) = 0;
- virtual void DoesNotMatch(const Ch* str, SizeType length) = 0;
- virtual void DisallowedItem(SizeType index) = 0;
- virtual void TooFewItems(SizeType actualCount, SizeType expectedCount) = 0;
- virtual void TooManyItems(SizeType actualCount, SizeType expectedCount) = 0;
- virtual void DuplicateItems(SizeType index1, SizeType index2) = 0;
- virtual void TooManyProperties(SizeType actualCount, SizeType expectedCount) = 0;
- virtual void TooFewProperties(SizeType actualCount, SizeType expectedCount) = 0;
- virtual void StartMissingProperties() = 0;
- virtual void AddMissingProperty(const SValue& name) = 0;
- virtual bool EndMissingProperties() = 0;
- virtual void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) = 0;
- virtual void DisallowedProperty(const Ch* name, SizeType length) = 0;
- virtual void StartDependencyErrors() = 0;
- virtual void StartMissingDependentProperties() = 0;
- virtual void AddMissingDependentProperty(const SValue& targetName) = 0;
- virtual void EndMissingDependentProperties(const SValue& sourceName) = 0;
- virtual void AddDependencySchemaError(const SValue& souceName, ISchemaValidator* subvalidator) = 0;
- virtual bool EndDependencyErrors() = 0;
- virtual void DisallowedValue(const ValidateErrorCode code) = 0;
- virtual void StartDisallowedType() = 0;
- virtual void AddExpectedType(const typename SchemaType::ValueType& expectedType) = 0;
- virtual void EndDisallowedType(const typename SchemaType::ValueType& actualType) = 0;
- virtual void NotAllOf(ISchemaValidator** subvalidators, SizeType count) = 0;
- virtual void NoneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
- virtual void NotOneOf(ISchemaValidator** subvalidators, SizeType count) = 0;
- virtual void MultipleOneOf(SizeType index1, SizeType index2) = 0;
- virtual void Disallowed() = 0;
- virtual void DisallowedWhenWriting() = 0;
- virtual void DisallowedWhenReading() = 0;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Hasher
- // For comparison of compound value
- template<typename Encoding, typename Allocator>
- class Hasher {
- public:
- typedef typename Encoding::Ch Ch;
- Hasher(Allocator* allocator = 0, size_t stackCapacity = kDefaultSize) : stack_(allocator, stackCapacity) {}
- bool Null() { return WriteType(kNullType); }
- bool Bool(bool b) { return WriteType(b ? kTrueType : kFalseType); }
- bool Int(int i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
- bool Uint(unsigned u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
- bool Int64(int64_t i) { Number n; n.u.i = i; n.d = static_cast<double>(i); return WriteNumber(n); }
- bool Uint64(uint64_t u) { Number n; n.u.u = u; n.d = static_cast<double>(u); return WriteNumber(n); }
- bool Double(double d) {
- Number n;
- if (d < 0) n.u.i = static_cast<int64_t>(d);
- else n.u.u = static_cast<uint64_t>(d);
- n.d = d;
- return WriteNumber(n);
- }
- bool RawNumber(const Ch* str, SizeType len, bool) {
- WriteBuffer(kNumberType, str, len * sizeof(Ch));
- return true;
- }
- bool String(const Ch* str, SizeType len, bool) {
- WriteBuffer(kStringType, str, len * sizeof(Ch));
- return true;
- }
- bool StartObject() { return true; }
- bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); }
- bool EndObject(SizeType memberCount) {
- uint64_t h = Hash(0, kObjectType);
- uint64_t* kv = stack_.template Pop<uint64_t>(memberCount * 2);
- for (SizeType i = 0; i < memberCount; i++)
- // Issue #2205
- // Hasing the key to avoid key=value cases with bug-prone zero-value hash
- h ^= Hash(Hash(0, kv[i * 2]), kv[i * 2 + 1]); // Use xor to achieve member order insensitive
- *stack_.template Push<uint64_t>() = h;
- return true;
- }
-
- bool StartArray() { return true; }
- bool EndArray(SizeType elementCount) {
- uint64_t h = Hash(0, kArrayType);
- uint64_t* e = stack_.template Pop<uint64_t>(elementCount);
- for (SizeType i = 0; i < elementCount; i++)
- h = Hash(h, e[i]); // Use hash to achieve element order sensitive
- *stack_.template Push<uint64_t>() = h;
- return true;
- }
- bool IsValid() const { return stack_.GetSize() == sizeof(uint64_t); }
- uint64_t GetHashCode() const {
- RAPIDJSON_ASSERT(IsValid());
- return *stack_.template Top<uint64_t>();
- }
- private:
- static const size_t kDefaultSize = 256;
- struct Number {
- union U {
- uint64_t u;
- int64_t i;
- }u;
- double d;
- };
- bool WriteType(Type type) { return WriteBuffer(type, 0, 0); }
-
- bool WriteNumber(const Number& n) { return WriteBuffer(kNumberType, &n, sizeof(n)); }
-
- bool WriteBuffer(Type type, const void* data, size_t len) {
- // FNV-1a from http://isthe.com/chongo/tech/comp/fnv/
- uint64_t h = Hash(RAPIDJSON_UINT64_C2(0xcbf29ce4, 0x84222325), type);
- const unsigned char* d = static_cast<const unsigned char*>(data);
- for (size_t i = 0; i < len; i++)
- h = Hash(h, d[i]);
- *stack_.template Push<uint64_t>() = h;
- return true;
- }
- static uint64_t Hash(uint64_t h, uint64_t d) {
- static const uint64_t kPrime = RAPIDJSON_UINT64_C2(0x00000100, 0x000001b3);
- h ^= d;
- h *= kPrime;
- return h;
- }
- Stack<Allocator> stack_;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // SchemaValidationContext
- template <typename SchemaDocumentType>
- struct SchemaValidationContext {
- typedef Schema<SchemaDocumentType> SchemaType;
- typedef ISchemaStateFactory<SchemaType> SchemaValidatorFactoryType;
- typedef IValidationErrorHandler<SchemaType> ErrorHandlerType;
- typedef typename SchemaType::ValueType ValueType;
- typedef typename ValueType::Ch Ch;
- enum PatternValidatorType {
- kPatternValidatorOnly,
- kPatternValidatorWithProperty,
- kPatternValidatorWithAdditionalProperty
- };
- SchemaValidationContext(SchemaValidatorFactoryType& f, ErrorHandlerType& eh, const SchemaType* s, unsigned fl = 0) :
- factory(f),
- error_handler(eh),
- schema(s),
- flags(fl),
- valueSchema(),
- invalidKeyword(),
- invalidCode(),
- hasher(),
- arrayElementHashCodes(),
- validators(),
- validatorCount(),
- patternPropertiesValidators(),
- patternPropertiesValidatorCount(),
- patternPropertiesSchemas(),
- patternPropertiesSchemaCount(),
- valuePatternValidatorType(kPatternValidatorOnly),
- propertyExist(),
- inArray(false),
- valueUniqueness(false),
- arrayUniqueness(false)
- {
- }
- ~SchemaValidationContext() {
- if (hasher)
- factory.DestroryHasher(hasher);
- if (validators) {
- for (SizeType i = 0; i < validatorCount; i++) {
- if (validators[i]) {
- factory.DestroySchemaValidator(validators[i]);
- }
- }
- factory.FreeState(validators);
- }
- if (patternPropertiesValidators) {
- for (SizeType i = 0; i < patternPropertiesValidatorCount; i++) {
- if (patternPropertiesValidators[i]) {
- factory.DestroySchemaValidator(patternPropertiesValidators[i]);
- }
- }
- factory.FreeState(patternPropertiesValidators);
- }
- if (patternPropertiesSchemas)
- factory.FreeState(patternPropertiesSchemas);
- if (propertyExist)
- factory.FreeState(propertyExist);
- }
- SchemaValidatorFactoryType& factory;
- ErrorHandlerType& error_handler;
- const SchemaType* schema;
- unsigned flags;
- const SchemaType* valueSchema;
- const Ch* invalidKeyword;
- ValidateErrorCode invalidCode;
- void* hasher; // Only validator access
- void* arrayElementHashCodes; // Only validator access this
- ISchemaValidator** validators;
- SizeType validatorCount;
- ISchemaValidator** patternPropertiesValidators;
- SizeType patternPropertiesValidatorCount;
- const SchemaType** patternPropertiesSchemas;
- SizeType patternPropertiesSchemaCount;
- PatternValidatorType valuePatternValidatorType;
- PatternValidatorType objectPatternValidatorType;
- SizeType arrayElementIndex;
- bool* propertyExist;
- bool inArray;
- bool valueUniqueness;
- bool arrayUniqueness;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // Schema
- template <typename SchemaDocumentType>
- class Schema {
- public:
- typedef typename SchemaDocumentType::ValueType ValueType;
- typedef typename SchemaDocumentType::AllocatorType AllocatorType;
- typedef typename SchemaDocumentType::PointerType PointerType;
- typedef typename ValueType::EncodingType EncodingType;
- typedef typename EncodingType::Ch Ch;
- typedef SchemaValidationContext<SchemaDocumentType> Context;
- typedef Schema<SchemaDocumentType> SchemaType;
- typedef GenericValue<EncodingType, AllocatorType> SValue;
- typedef IValidationErrorHandler<Schema> ErrorHandler;
- typedef GenericUri<ValueType, AllocatorType> UriType;
- friend class GenericSchemaDocument<ValueType, AllocatorType>;
- Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
- allocator_(allocator),
- uri_(schemaDocument->GetURI(), *allocator),
- id_(id, allocator),
- spec_(schemaDocument->GetSpecification()),
- pointer_(p, allocator),
- typeless_(schemaDocument->GetTypeless()),
- enum_(),
- enumCount_(),
- not_(),
- type_((1 << kTotalSchemaType) - 1), // typeless
- validatorCount_(),
- notValidatorIndex_(),
- properties_(),
- additionalPropertiesSchema_(),
- patternProperties_(),
- patternPropertyCount_(),
- propertyCount_(),
- minProperties_(),
- maxProperties_(SizeType(~0)),
- additionalProperties_(true),
- hasDependencies_(),
- hasRequired_(),
- hasSchemaDependencies_(),
- additionalItemsSchema_(),
- itemsList_(),
- itemsTuple_(),
- itemsTupleCount_(),
- minItems_(),
- maxItems_(SizeType(~0)),
- additionalItems_(true),
- uniqueItems_(false),
- pattern_(),
- minLength_(0),
- maxLength_(~SizeType(0)),
- exclusiveMinimum_(false),
- exclusiveMaximum_(false),
- defaultValueLength_(0),
- readOnly_(false),
- writeOnly_(false),
- nullable_(false)
- {
- GenericStringBuffer<EncodingType> sb;
- p.StringifyUriFragment(sb);
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Schema", sb.GetString(), id.GetString());
- typedef typename ValueType::ConstValueIterator ConstValueIterator;
- typedef typename ValueType::ConstMemberIterator ConstMemberIterator;
- // PR #1393
- // Early add this Schema and its $ref(s) in schemaDocument's map to avoid infinite
- // recursion (with recursive schemas), since schemaDocument->getSchema() is always
- // checked before creating a new one. Don't cache typeless_, though.
- if (this != typeless_) {
- typedef typename SchemaDocumentType::SchemaEntry SchemaEntry;
- SchemaEntry *entry = schemaDocument->schemaMap_.template Push<SchemaEntry>();
- new (entry) SchemaEntry(pointer_, this, true, allocator_);
- schemaDocument->AddSchemaRefs(this);
- }
- if (!value.IsObject())
- return;
- // If we have an id property, resolve it with the in-scope id
- // Not supported for open api 2.0 or 3.0
- if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
- if (const ValueType* v = GetMember(value, GetIdString())) {
- if (v->IsString()) {
- UriType local(*v, allocator);
- id_ = local.Resolve(id_, allocator);
- RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), v->GetString(), id_.GetString());
- }
- }
- if (const ValueType* v = GetMember(value, GetTypeString())) {
- type_ = 0;
- if (v->IsString())
- AddType(*v);
- else if (v->IsArray())
- for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr)
- AddType(*itr);
- }
- if (const ValueType* v = GetMember(value, GetEnumString())) {
- if (v->IsArray() && v->Size() > 0) {
- enum_ = static_cast<uint64_t*>(allocator_->Malloc(sizeof(uint64_t) * v->Size()));
- for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr) {
- typedef Hasher<EncodingType, MemoryPoolAllocator<AllocatorType> > EnumHasherType;
- char buffer[256u + 24];
- MemoryPoolAllocator<AllocatorType> hasherAllocator(buffer, sizeof(buffer));
- EnumHasherType h(&hasherAllocator, 256);
- itr->Accept(h);
- enum_[enumCount_++] = h.GetHashCode();
- }
- }
- }
- if (schemaDocument)
- AssignIfExist(allOf_, *schemaDocument, p, value, GetAllOfString(), document);
- // AnyOf, OneOf, Not not supported for open api 2.0
- if (schemaDocument && spec_.oapi != kVersion20) {
- AssignIfExist(anyOf_, *schemaDocument, p, value, GetAnyOfString(), document);
- AssignIfExist(oneOf_, *schemaDocument, p, value, GetOneOfString(), document);
- if (const ValueType* v = GetMember(value, GetNotString())) {
- schemaDocument->CreateSchema(¬_, p.Append(GetNotString(), allocator_), *v, document, id_);
- notValidatorIndex_ = validatorCount_;
- validatorCount_++;
- }
- }
- // Object
- const ValueType* properties = GetMember(value, GetPropertiesString());
- const ValueType* required = GetMember(value, GetRequiredString());
- const ValueType* dependencies = GetMember(value, GetDependenciesString());
- {
- // Gather properties from properties/required/dependencies
- SValue allProperties(kArrayType);
- if (properties && properties->IsObject())
- for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr)
- AddUniqueElement(allProperties, itr->name);
- if (required && required->IsArray())
- for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
- if (itr->IsString())
- AddUniqueElement(allProperties, *itr);
- // Dependencies not supported for open api 2.0 and 3.0
- if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
- if (dependencies && dependencies->IsObject())
- for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
- AddUniqueElement(allProperties, itr->name);
- if (itr->value.IsArray())
- for (ConstValueIterator i = itr->value.Begin(); i != itr->value.End(); ++i)
- if (i->IsString())
- AddUniqueElement(allProperties, *i);
- }
- if (allProperties.Size() > 0) {
- propertyCount_ = allProperties.Size();
- properties_ = static_cast<Property*>(allocator_->Malloc(sizeof(Property) * propertyCount_));
- for (SizeType i = 0; i < propertyCount_; i++) {
- new (&properties_[i]) Property();
- properties_[i].name = allProperties[i];
- properties_[i].schema = typeless_;
- }
- }
- }
- if (properties && properties->IsObject()) {
- PointerType q = p.Append(GetPropertiesString(), allocator_);
- for (ConstMemberIterator itr = properties->MemberBegin(); itr != properties->MemberEnd(); ++itr) {
- SizeType index;
- if (FindPropertyIndex(itr->name, &index))
- schemaDocument->CreateSchema(&properties_[index].schema, q.Append(itr->name, allocator_), itr->value, document, id_);
- }
- }
- // PatternProperties not supported for open api 2.0 and 3.0
- if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
- if (const ValueType* v = GetMember(value, GetPatternPropertiesString())) {
- PointerType q = p.Append(GetPatternPropertiesString(), allocator_);
- patternProperties_ = static_cast<PatternProperty*>(allocator_->Malloc(sizeof(PatternProperty) * v->MemberCount()));
- patternPropertyCount_ = 0;
- for (ConstMemberIterator itr = v->MemberBegin(); itr != v->MemberEnd(); ++itr) {
- new (&patternProperties_[patternPropertyCount_]) PatternProperty();
- PointerType r = q.Append(itr->name, allocator_);
- patternProperties_[patternPropertyCount_].pattern = CreatePattern(itr->name, schemaDocument, r);
- schemaDocument->CreateSchema(&patternProperties_[patternPropertyCount_].schema, r, itr->value, document, id_);
- patternPropertyCount_++;
- }
- }
- if (required && required->IsArray())
- for (ConstValueIterator itr = required->Begin(); itr != required->End(); ++itr)
- if (itr->IsString()) {
- SizeType index;
- if (FindPropertyIndex(*itr, &index)) {
- properties_[index].required = true;
- hasRequired_ = true;
- }
- }
- // Dependencies not supported for open api 2.0 and 3.0
- if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
- if (dependencies && dependencies->IsObject()) {
- PointerType q = p.Append(GetDependenciesString(), allocator_);
- hasDependencies_ = true;
- for (ConstMemberIterator itr = dependencies->MemberBegin(); itr != dependencies->MemberEnd(); ++itr) {
- SizeType sourceIndex;
- if (FindPropertyIndex(itr->name, &sourceIndex)) {
- if (itr->value.IsArray()) {
- properties_[sourceIndex].dependencies = static_cast<bool*>(allocator_->Malloc(sizeof(bool) * propertyCount_));
- std::memset(properties_[sourceIndex].dependencies, 0, sizeof(bool)* propertyCount_);
- for (ConstValueIterator targetItr = itr->value.Begin(); targetItr != itr->value.End(); ++targetItr) {
- SizeType targetIndex;
- if (FindPropertyIndex(*targetItr, &targetIndex))
- properties_[sourceIndex].dependencies[targetIndex] = true;
- }
- }
- else if (itr->value.IsObject()) {
- hasSchemaDependencies_ = true;
- schemaDocument->CreateSchema(&properties_[sourceIndex].dependenciesSchema, q.Append(itr->name, allocator_), itr->value, document, id_);
- properties_[sourceIndex].dependenciesValidatorIndex = validatorCount_;
- validatorCount_++;
- }
- }
- }
- }
- if (const ValueType* v = GetMember(value, GetAdditionalPropertiesString())) {
- if (v->IsBool())
- additionalProperties_ = v->GetBool();
- else if (v->IsObject())
- schemaDocument->CreateSchema(&additionalPropertiesSchema_, p.Append(GetAdditionalPropertiesString(), allocator_), *v, document, id_);
- }
- AssignIfExist(minProperties_, value, GetMinPropertiesString());
- AssignIfExist(maxProperties_, value, GetMaxPropertiesString());
- // Array
- if (const ValueType* v = GetMember(value, GetItemsString())) {
- PointerType q = p.Append(GetItemsString(), allocator_);
- if (v->IsObject()) // List validation
- schemaDocument->CreateSchema(&itemsList_, q, *v, document, id_);
- else if (v->IsArray()) { // Tuple validation
- itemsTuple_ = static_cast<const Schema**>(allocator_->Malloc(sizeof(const Schema*) * v->Size()));
- SizeType index = 0;
- for (ConstValueIterator itr = v->Begin(); itr != v->End(); ++itr, index++)
- schemaDocument->CreateSchema(&itemsTuple_[itemsTupleCount_++], q.Append(index, allocator_), *itr, document, id_);
- }
- }
- AssignIfExist(minItems_, value, GetMinItemsString());
- AssignIfExist(maxItems_, value, GetMaxItemsString());
- // AdditionalItems not supported for openapi 2.0 and 3.0
- if (spec_.oapi != kVersion20 && spec_.oapi != kVersion30)
- if (const ValueType* v = GetMember(value, GetAdditionalItemsString())) {
- if (v->IsBool())
- additionalItems_ = v->GetBool();
- else if (v->IsObject())
- schemaDocument->CreateSchema(&additionalItemsSchema_, p.Append(GetAdditionalItemsString(), allocator_), *v, document, id_);
- }
- AssignIfExist(uniqueItems_, value, GetUniqueItemsString());
- // String
- AssignIfExist(minLength_, value, GetMinLengthString());
- AssignIfExist(maxLength_, value, GetMaxLengthString());
- if (const ValueType* v = GetMember(value, GetPatternString()))
- pattern_ = CreatePattern(*v, schemaDocument, p.Append(GetPatternString(), allocator_));
- // Number
- if (const ValueType* v = GetMember(value, GetMinimumString()))
- if (v->IsNumber())
- minimum_.CopyFrom(*v, *allocator_);
- if (const ValueType* v = GetMember(value, GetMaximumString()))
- if (v->IsNumber())
- maximum_.CopyFrom(*v, *allocator_);
- AssignIfExist(exclusiveMinimum_, value, GetExclusiveMinimumString());
- AssignIfExist(exclusiveMaximum_, value, GetExclusiveMaximumString());
- if (const ValueType* v = GetMember(value, GetMultipleOfString()))
- if (v->IsNumber() && v->GetDouble() > 0.0)
- multipleOf_.CopyFrom(*v, *allocator_);
- // Default
- if (const ValueType* v = GetMember(value, GetDefaultValueString()))
- if (v->IsString())
- defaultValueLength_ = v->GetStringLength();
- // ReadOnly - open api only (until draft 7 supported)
- // WriteOnly - open api 3 only (until draft 7 supported)
- // Both can't be true
- if (spec_.oapi != kVersionNone)
- AssignIfExist(readOnly_, value, GetReadOnlyString());
- if (spec_.oapi >= kVersion30)
- AssignIfExist(writeOnly_, value, GetWriteOnlyString());
- if (readOnly_ && writeOnly_)
- schemaDocument->SchemaError(kSchemaErrorReadOnlyAndWriteOnly, p);
- // Nullable - open api 3 only
- // If true add 'null' as allowable type
- if (spec_.oapi >= kVersion30) {
- AssignIfExist(nullable_, value, GetNullableString());
- if (nullable_)
- AddType(GetNullString());
- }
- }
- ~Schema() {
- AllocatorType::Free(enum_);
- if (properties_) {
- for (SizeType i = 0; i < propertyCount_; i++)
- properties_[i].~Property();
- AllocatorType::Free(properties_);
- }
- if (patternProperties_) {
- for (SizeType i = 0; i < patternPropertyCount_; i++)
- patternProperties_[i].~PatternProperty();
- AllocatorType::Free(patternProperties_);
- }
- AllocatorType::Free(itemsTuple_);
- #if RAPIDJSON_SCHEMA_HAS_REGEX
- if (pattern_) {
- pattern_->~RegexType();
- AllocatorType::Free(pattern_);
- }
- #endif
- }
- const SValue& GetURI() const {
- return uri_;
- }
- const UriType& GetId() const {
- return id_;
- }
- const Specification& GetSpecification() const {
- return spec_;
- }
- const PointerType& GetPointer() const {
- return pointer_;
- }
- bool BeginValue(Context& context) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::BeginValue");
- if (context.inArray) {
- if (uniqueItems_)
- context.valueUniqueness = true;
- if (itemsList_)
- context.valueSchema = itemsList_;
- else if (itemsTuple_) {
- if (context.arrayElementIndex < itemsTupleCount_)
- context.valueSchema = itemsTuple_[context.arrayElementIndex];
- else if (additionalItemsSchema_)
- context.valueSchema = additionalItemsSchema_;
- else if (additionalItems_)
- context.valueSchema = typeless_;
- else {
- context.error_handler.DisallowedItem(context.arrayElementIndex);
- // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
- context.valueSchema = typeless_;
- // Must bump arrayElementIndex for when kValidateContinueOnErrorFlag is set
- context.arrayElementIndex++;
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalItems);
- }
- }
- else
- context.valueSchema = typeless_;
- context.arrayElementIndex++;
- }
- return true;
- }
- RAPIDJSON_FORCEINLINE bool EndValue(Context& context) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndValue");
- // Only check pattern properties if we have validators
- if (context.patternPropertiesValidatorCount > 0) {
- bool otherValid = false;
- SizeType count = context.patternPropertiesValidatorCount;
- if (context.objectPatternValidatorType != Context::kPatternValidatorOnly)
- otherValid = context.patternPropertiesValidators[--count]->IsValid();
- bool patternValid = true;
- for (SizeType i = 0; i < count; i++)
- if (!context.patternPropertiesValidators[i]->IsValid()) {
- patternValid = false;
- break;
- }
- if (context.objectPatternValidatorType == Context::kPatternValidatorOnly) {
- if (!patternValid) {
- context.error_handler.PropertyViolations(context.patternPropertiesValidators, count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
- }
- }
- else if (context.objectPatternValidatorType == Context::kPatternValidatorWithProperty) {
- if (!patternValid || !otherValid) {
- context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
- }
- }
- else if (!patternValid && !otherValid) { // kPatternValidatorWithAdditionalProperty)
- context.error_handler.PropertyViolations(context.patternPropertiesValidators, count + 1);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPatternProperties);
- }
- }
- // For enums only check if we have a hasher
- if (enum_ && context.hasher) {
- const uint64_t h = context.factory.GetHashCode(context.hasher);
- for (SizeType i = 0; i < enumCount_; i++)
- if (enum_[i] == h)
- goto foundEnum;
- context.error_handler.DisallowedValue(kValidateErrorEnum);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorEnum);
- foundEnum:;
- }
- // Only check allOf etc if we have validators
- if (context.validatorCount > 0) {
- if (allOf_.schemas)
- for (SizeType i = allOf_.begin; i < allOf_.begin + allOf_.count; i++)
- if (!context.validators[i]->IsValid()) {
- context.error_handler.NotAllOf(&context.validators[allOf_.begin], allOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAllOf);
- }
- if (anyOf_.schemas) {
- for (SizeType i = anyOf_.begin; i < anyOf_.begin + anyOf_.count; i++)
- if (context.validators[i]->IsValid())
- goto foundAny;
- context.error_handler.NoneOf(&context.validators[anyOf_.begin], anyOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAnyOf);
- foundAny:;
- }
- if (oneOf_.schemas) {
- bool oneValid = false;
- SizeType firstMatch = 0;
- for (SizeType i = oneOf_.begin; i < oneOf_.begin + oneOf_.count; i++)
- if (context.validators[i]->IsValid()) {
- if (oneValid) {
- context.error_handler.MultipleOneOf(firstMatch, i - oneOf_.begin);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOfMatch);
- } else {
- oneValid = true;
- firstMatch = i - oneOf_.begin;
- }
- }
- if (!oneValid) {
- context.error_handler.NotOneOf(&context.validators[oneOf_.begin], oneOf_.count);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorOneOf);
- }
- }
- if (not_ && context.validators[notValidatorIndex_]->IsValid()) {
- context.error_handler.Disallowed();
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorNot);
- }
- }
- return true;
- }
- bool Null(Context& context) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Null");
- if (!(type_ & (1 << kNullSchemaType))) {
- DisallowedType(context, GetNullString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- return CreateParallelValidator(context);
- }
- bool Bool(Context& context, bool b) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Bool", b);
- if (!CheckBool(context, b))
- return false;
- return CreateParallelValidator(context);
- }
- bool Int(Context& context, int i) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int", (int64_t)i);
- if (!CheckInt(context, i))
- return false;
- return CreateParallelValidator(context);
- }
- bool Uint(Context& context, unsigned u) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint", (uint64_t)u);
- if (!CheckUint(context, u))
- return false;
- return CreateParallelValidator(context);
- }
- bool Int64(Context& context, int64_t i) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Int64", i);
- if (!CheckInt(context, i))
- return false;
- return CreateParallelValidator(context);
- }
- bool Uint64(Context& context, uint64_t u) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Uint64", u);
- if (!CheckUint(context, u))
- return false;
- return CreateParallelValidator(context);
- }
- bool Double(Context& context, double d) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Double", d);
- if (!(type_ & (1 << kNumberSchemaType))) {
- DisallowedType(context, GetNumberString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- if (!minimum_.IsNull() && !CheckDoubleMinimum(context, d))
- return false;
- if (!maximum_.IsNull() && !CheckDoubleMaximum(context, d))
- return false;
- if (!multipleOf_.IsNull() && !CheckDoubleMultipleOf(context, d))
- return false;
- return CreateParallelValidator(context);
- }
- bool String(Context& context, const Ch* str, SizeType length, bool) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::String", str);
- if (!(type_ & (1 << kStringSchemaType))) {
- DisallowedType(context, GetStringString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- if (minLength_ != 0 || maxLength_ != SizeType(~0)) {
- SizeType count;
- if (internal::CountStringCodePoint<EncodingType>(str, length, &count)) {
- if (count < minLength_) {
- context.error_handler.TooShort(str, length, minLength_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinLength);
- }
- if (count > maxLength_) {
- context.error_handler.TooLong(str, length, maxLength_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxLength);
- }
- }
- }
- if (pattern_ && !IsPatternMatch(pattern_, str, length)) {
- context.error_handler.DoesNotMatch(str, length);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorPattern);
- }
- return CreateParallelValidator(context);
- }
- bool StartObject(Context& context) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartObject");
- if (!(type_ & (1 << kObjectSchemaType))) {
- DisallowedType(context, GetObjectString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- if (hasDependencies_ || hasRequired_) {
- context.propertyExist = static_cast<bool*>(context.factory.MallocState(sizeof(bool) * propertyCount_));
- std::memset(context.propertyExist, 0, sizeof(bool) * propertyCount_);
- }
- if (patternProperties_) { // pre-allocate schema array
- SizeType count = patternPropertyCount_ + 1; // extra for valuePatternValidatorType
- context.patternPropertiesSchemas = static_cast<const SchemaType**>(context.factory.MallocState(sizeof(const SchemaType*) * count));
- context.patternPropertiesSchemaCount = 0;
- std::memset(context.patternPropertiesSchemas, 0, sizeof(SchemaType*) * count);
- }
- return CreateParallelValidator(context);
- }
- bool Key(Context& context, const Ch* str, SizeType len, bool) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::Key", str);
- if (patternProperties_) {
- context.patternPropertiesSchemaCount = 0;
- for (SizeType i = 0; i < patternPropertyCount_; i++)
- if (patternProperties_[i].pattern && IsPatternMatch(patternProperties_[i].pattern, str, len)) {
- context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = patternProperties_[i].schema;
- context.valueSchema = typeless_;
- }
- }
- SizeType index = 0;
- if (FindPropertyIndex(ValueType(str, len).Move(), &index)) {
- if (context.patternPropertiesSchemaCount > 0) {
- context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = properties_[index].schema;
- context.valueSchema = typeless_;
- context.valuePatternValidatorType = Context::kPatternValidatorWithProperty;
- }
- else
- context.valueSchema = properties_[index].schema;
- if (context.propertyExist)
- context.propertyExist[index] = true;
- return true;
- }
- if (additionalPropertiesSchema_) {
- if (context.patternPropertiesSchemaCount > 0) {
- context.patternPropertiesSchemas[context.patternPropertiesSchemaCount++] = additionalPropertiesSchema_;
- context.valueSchema = typeless_;
- context.valuePatternValidatorType = Context::kPatternValidatorWithAdditionalProperty;
- }
- else
- context.valueSchema = additionalPropertiesSchema_;
- return true;
- }
- else if (additionalProperties_) {
- context.valueSchema = typeless_;
- return true;
- }
- if (context.patternPropertiesSchemaCount == 0) { // patternProperties are not additional properties
- // Must set valueSchema for when kValidateContinueOnErrorFlag is set, else reports spurious type error
- context.valueSchema = typeless_;
- context.error_handler.DisallowedProperty(str, len);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorAdditionalProperties);
- }
- return true;
- }
- bool EndObject(Context& context, SizeType memberCount) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndObject");
- if (hasRequired_) {
- context.error_handler.StartMissingProperties();
- for (SizeType index = 0; index < propertyCount_; index++)
- if (properties_[index].required && !context.propertyExist[index])
- if (properties_[index].schema->defaultValueLength_ == 0 )
- context.error_handler.AddMissingProperty(properties_[index].name);
- if (context.error_handler.EndMissingProperties())
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorRequired);
- }
- if (memberCount < minProperties_) {
- context.error_handler.TooFewProperties(memberCount, minProperties_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinProperties);
- }
- if (memberCount > maxProperties_) {
- context.error_handler.TooManyProperties(memberCount, maxProperties_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxProperties);
- }
- if (hasDependencies_) {
- context.error_handler.StartDependencyErrors();
- for (SizeType sourceIndex = 0; sourceIndex < propertyCount_; sourceIndex++) {
- const Property& source = properties_[sourceIndex];
- if (context.propertyExist[sourceIndex]) {
- if (source.dependencies) {
- context.error_handler.StartMissingDependentProperties();
- for (SizeType targetIndex = 0; targetIndex < propertyCount_; targetIndex++)
- if (source.dependencies[targetIndex] && !context.propertyExist[targetIndex])
- context.error_handler.AddMissingDependentProperty(properties_[targetIndex].name);
- context.error_handler.EndMissingDependentProperties(source.name);
- }
- else if (source.dependenciesSchema) {
- ISchemaValidator* dependenciesValidator = context.validators[source.dependenciesValidatorIndex];
- if (!dependenciesValidator->IsValid())
- context.error_handler.AddDependencySchemaError(source.name, dependenciesValidator);
- }
- }
- }
- if (context.error_handler.EndDependencyErrors())
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorDependencies);
- }
- return true;
- }
- bool StartArray(Context& context) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::StartArray");
- context.arrayElementIndex = 0;
- context.inArray = true; // Ensure we note that we are in an array
- if (!(type_ & (1 << kArraySchemaType))) {
- DisallowedType(context, GetArrayString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- return CreateParallelValidator(context);
- }
- bool EndArray(Context& context, SizeType elementCount) const {
- RAPIDJSON_SCHEMA_PRINT(Method, "Schema::EndArray");
- context.inArray = false;
- if (elementCount < minItems_) {
- context.error_handler.TooFewItems(elementCount, minItems_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMinItems);
- }
- if (elementCount > maxItems_) {
- context.error_handler.TooManyItems(elementCount, maxItems_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMaxItems);
- }
- return true;
- }
- static const ValueType& GetValidateErrorKeyword(ValidateErrorCode validateErrorCode) {
- switch (validateErrorCode) {
- case kValidateErrorMultipleOf: return GetMultipleOfString();
- case kValidateErrorMaximum: return GetMaximumString();
- case kValidateErrorExclusiveMaximum: return GetMaximumString(); // Same
- case kValidateErrorMinimum: return GetMinimumString();
- case kValidateErrorExclusiveMinimum: return GetMinimumString(); // Same
- case kValidateErrorMaxLength: return GetMaxLengthString();
- case kValidateErrorMinLength: return GetMinLengthString();
- case kValidateErrorPattern: return GetPatternString();
- case kValidateErrorMaxItems: return GetMaxItemsString();
- case kValidateErrorMinItems: return GetMinItemsString();
- case kValidateErrorUniqueItems: return GetUniqueItemsString();
- case kValidateErrorAdditionalItems: return GetAdditionalItemsString();
- case kValidateErrorMaxProperties: return GetMaxPropertiesString();
- case kValidateErrorMinProperties: return GetMinPropertiesString();
- case kValidateErrorRequired: return GetRequiredString();
- case kValidateErrorAdditionalProperties: return GetAdditionalPropertiesString();
- case kValidateErrorPatternProperties: return GetPatternPropertiesString();
- case kValidateErrorDependencies: return GetDependenciesString();
- case kValidateErrorEnum: return GetEnumString();
- case kValidateErrorType: return GetTypeString();
- case kValidateErrorOneOf: return GetOneOfString();
- case kValidateErrorOneOfMatch: return GetOneOfString(); // Same
- case kValidateErrorAllOf: return GetAllOfString();
- case kValidateErrorAnyOf: return GetAnyOfString();
- case kValidateErrorNot: return GetNotString();
- case kValidateErrorReadOnly: return GetReadOnlyString();
- case kValidateErrorWriteOnly: return GetWriteOnlyString();
- default: return GetNullString();
- }
- }
- // Generate functions for string literal according to Ch
- #define RAPIDJSON_STRING_(name, ...) \
- static const ValueType& Get##name##String() {\
- static const Ch s[] = { __VA_ARGS__, '\0' };\
- static const ValueType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1));\
- return v;\
- }
- RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
- RAPIDJSON_STRING_(Boolean, 'b', 'o', 'o', 'l', 'e', 'a', 'n')
- RAPIDJSON_STRING_(Object, 'o', 'b', 'j', 'e', 'c', 't')
- RAPIDJSON_STRING_(Array, 'a', 'r', 'r', 'a', 'y')
- RAPIDJSON_STRING_(String, 's', 't', 'r', 'i', 'n', 'g')
- RAPIDJSON_STRING_(Number, 'n', 'u', 'm', 'b', 'e', 'r')
- RAPIDJSON_STRING_(Integer, 'i', 'n', 't', 'e', 'g', 'e', 'r')
- RAPIDJSON_STRING_(Type, 't', 'y', 'p', 'e')
- RAPIDJSON_STRING_(Enum, 'e', 'n', 'u', 'm')
- RAPIDJSON_STRING_(AllOf, 'a', 'l', 'l', 'O', 'f')
- RAPIDJSON_STRING_(AnyOf, 'a', 'n', 'y', 'O', 'f')
- RAPIDJSON_STRING_(OneOf, 'o', 'n', 'e', 'O', 'f')
- RAPIDJSON_STRING_(Not, 'n', 'o', 't')
- RAPIDJSON_STRING_(Properties, 'p', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
- RAPIDJSON_STRING_(Required, 'r', 'e', 'q', 'u', 'i', 'r', 'e', 'd')
- RAPIDJSON_STRING_(Dependencies, 'd', 'e', 'p', 'e', 'n', 'd', 'e', 'n', 'c', 'i', 'e', 's')
- RAPIDJSON_STRING_(PatternProperties, 'p', 'a', 't', 't', 'e', 'r', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
- RAPIDJSON_STRING_(AdditionalProperties, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
- RAPIDJSON_STRING_(MinProperties, 'm', 'i', 'n', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
- RAPIDJSON_STRING_(MaxProperties, 'm', 'a', 'x', 'P', 'r', 'o', 'p', 'e', 'r', 't', 'i', 'e', 's')
- RAPIDJSON_STRING_(Items, 'i', 't', 'e', 'm', 's')
- RAPIDJSON_STRING_(MinItems, 'm', 'i', 'n', 'I', 't', 'e', 'm', 's')
- RAPIDJSON_STRING_(MaxItems, 'm', 'a', 'x', 'I', 't', 'e', 'm', 's')
- RAPIDJSON_STRING_(AdditionalItems, 'a', 'd', 'd', 'i', 't', 'i', 'o', 'n', 'a', 'l', 'I', 't', 'e', 'm', 's')
- RAPIDJSON_STRING_(UniqueItems, 'u', 'n', 'i', 'q', 'u', 'e', 'I', 't', 'e', 'm', 's')
- RAPIDJSON_STRING_(MinLength, 'm', 'i', 'n', 'L', 'e', 'n', 'g', 't', 'h')
- RAPIDJSON_STRING_(MaxLength, 'm', 'a', 'x', 'L', 'e', 'n', 'g', 't', 'h')
- RAPIDJSON_STRING_(Pattern, 'p', 'a', 't', 't', 'e', 'r', 'n')
- RAPIDJSON_STRING_(Minimum, 'm', 'i', 'n', 'i', 'm', 'u', 'm')
- RAPIDJSON_STRING_(Maximum, 'm', 'a', 'x', 'i', 'm', 'u', 'm')
- RAPIDJSON_STRING_(ExclusiveMinimum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'i', 'n', 'i', 'm', 'u', 'm')
- RAPIDJSON_STRING_(ExclusiveMaximum, 'e', 'x', 'c', 'l', 'u', 's', 'i', 'v', 'e', 'M', 'a', 'x', 'i', 'm', 'u', 'm')
- RAPIDJSON_STRING_(MultipleOf, 'm', 'u', 'l', 't', 'i', 'p', 'l', 'e', 'O', 'f')
- RAPIDJSON_STRING_(DefaultValue, 'd', 'e', 'f', 'a', 'u', 'l', 't')
- RAPIDJSON_STRING_(Schema, '$', 's', 'c', 'h', 'e', 'm', 'a')
- RAPIDJSON_STRING_(Ref, '$', 'r', 'e', 'f')
- RAPIDJSON_STRING_(Id, 'i', 'd')
- RAPIDJSON_STRING_(Swagger, 's', 'w', 'a', 'g', 'g', 'e', 'r')
- RAPIDJSON_STRING_(OpenApi, 'o', 'p', 'e', 'n', 'a', 'p', 'i')
- RAPIDJSON_STRING_(ReadOnly, 'r', 'e', 'a', 'd', 'O', 'n', 'l', 'y')
- RAPIDJSON_STRING_(WriteOnly, 'w', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
- RAPIDJSON_STRING_(Nullable, 'n', 'u', 'l', 'l', 'a', 'b', 'l', 'e')
- #undef RAPIDJSON_STRING_
- private:
- enum SchemaValueType {
- kNullSchemaType,
- kBooleanSchemaType,
- kObjectSchemaType,
- kArraySchemaType,
- kStringSchemaType,
- kNumberSchemaType,
- kIntegerSchemaType,
- kTotalSchemaType
- };
- #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
- typedef internal::GenericRegex<EncodingType, AllocatorType> RegexType;
- #elif RAPIDJSON_SCHEMA_USE_STDREGEX
- typedef std::basic_regex<Ch> RegexType;
- #else
- typedef char RegexType;
- #endif
- struct SchemaArray {
- SchemaArray() : schemas(), count() {}
- ~SchemaArray() { AllocatorType::Free(schemas); }
- const SchemaType** schemas;
- SizeType begin; // begin index of context.validators
- SizeType count;
- };
- template <typename V1, typename V2>
- void AddUniqueElement(V1& a, const V2& v) {
- for (typename V1::ConstValueIterator itr = a.Begin(); itr != a.End(); ++itr)
- if (*itr == v)
- return;
- V1 c(v, *allocator_);
- a.PushBack(c, *allocator_);
- }
- static const ValueType* GetMember(const ValueType& value, const ValueType& name) {
- typename ValueType::ConstMemberIterator itr = value.FindMember(name);
- return itr != value.MemberEnd() ? &(itr->value) : 0;
- }
- static void AssignIfExist(bool& out, const ValueType& value, const ValueType& name) {
- if (const ValueType* v = GetMember(value, name))
- if (v->IsBool())
- out = v->GetBool();
- }
- static void AssignIfExist(SizeType& out, const ValueType& value, const ValueType& name) {
- if (const ValueType* v = GetMember(value, name))
- if (v->IsUint64() && v->GetUint64() <= SizeType(~0))
- out = static_cast<SizeType>(v->GetUint64());
- }
- void AssignIfExist(SchemaArray& out, SchemaDocumentType& schemaDocument, const PointerType& p, const ValueType& value, const ValueType& name, const ValueType& document) {
- if (const ValueType* v = GetMember(value, name)) {
- if (v->IsArray() && v->Size() > 0) {
- PointerType q = p.Append(name, allocator_);
- out.count = v->Size();
- out.schemas = static_cast<const Schema**>(allocator_->Malloc(out.count * sizeof(const Schema*)));
- memset(out.schemas, 0, sizeof(Schema*)* out.count);
- for (SizeType i = 0; i < out.count; i++)
- schemaDocument.CreateSchema(&out.schemas[i], q.Append(i, allocator_), (*v)[i], document, id_);
- out.begin = validatorCount_;
- validatorCount_ += out.count;
- }
- }
- }
- #if RAPIDJSON_SCHEMA_USE_INTERNALREGEX
- template <typename ValueType>
- RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
- if (value.IsString()) {
- RegexType* r = new (allocator_->Malloc(sizeof(RegexType))) RegexType(value.GetString(), allocator_);
- if (!r->IsValid()) {
- sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
- r->~RegexType();
- AllocatorType::Free(r);
- r = 0;
- }
- return r;
- }
- return 0;
- }
- static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType) {
- GenericRegexSearch<RegexType> rs(*pattern);
- return rs.Search(str);
- }
- #elif RAPIDJSON_SCHEMA_USE_STDREGEX
- template <typename ValueType>
- RegexType* CreatePattern(const ValueType& value, SchemaDocumentType* sd, const PointerType& p) {
- if (value.IsString()) {
- RegexType *r = static_cast<RegexType*>(allocator_->Malloc(sizeof(RegexType)));
- try {
- return new (r) RegexType(value.GetString(), std::size_t(value.GetStringLength()), std::regex_constants::ECMAScript);
- }
- catch (const std::regex_error& e) {
- sd->SchemaErrorValue(kSchemaErrorRegexInvalid, p, value.GetString(), value.GetStringLength());
- AllocatorType::Free(r);
- }
- }
- return 0;
- }
- static bool IsPatternMatch(const RegexType* pattern, const Ch *str, SizeType length) {
- std::match_results<const Ch*> r;
- return std::regex_search(str, str + length, r, *pattern);
- }
- #else
- template <typename ValueType>
- RegexType* CreatePattern(const ValueType&) {
- return 0;
- }
- static bool IsPatternMatch(const RegexType*, const Ch *, SizeType) { return true; }
- #endif // RAPIDJSON_SCHEMA_USE_STDREGEX
- void AddType(const ValueType& type) {
- if (type == GetNullString() ) type_ |= 1 << kNullSchemaType;
- else if (type == GetBooleanString()) type_ |= 1 << kBooleanSchemaType;
- else if (type == GetObjectString() ) type_ |= 1 << kObjectSchemaType;
- else if (type == GetArrayString() ) type_ |= 1 << kArraySchemaType;
- else if (type == GetStringString() ) type_ |= 1 << kStringSchemaType;
- else if (type == GetIntegerString()) type_ |= 1 << kIntegerSchemaType;
- else if (type == GetNumberString() ) type_ |= (1 << kNumberSchemaType) | (1 << kIntegerSchemaType);
- }
- // Creates parallel validators for allOf, anyOf, oneOf, not and schema dependencies, if required.
- // Also creates a hasher for enums and array uniqueness, if required.
- // Also a useful place to add type-independent error checks.
- bool CreateParallelValidator(Context& context) const {
- if (enum_ || context.arrayUniqueness)
- context.hasher = context.factory.CreateHasher();
- if (validatorCount_) {
- RAPIDJSON_ASSERT(context.validators == 0);
- context.validators = static_cast<ISchemaValidator**>(context.factory.MallocState(sizeof(ISchemaValidator*) * validatorCount_));
- std::memset(context.validators, 0, sizeof(ISchemaValidator*) * validatorCount_);
- context.validatorCount = validatorCount_;
- // Always return after first failure for these sub-validators
- if (allOf_.schemas)
- CreateSchemaValidators(context, allOf_, false);
- if (anyOf_.schemas)
- CreateSchemaValidators(context, anyOf_, false);
- if (oneOf_.schemas)
- CreateSchemaValidators(context, oneOf_, false);
- if (not_)
- context.validators[notValidatorIndex_] = context.factory.CreateSchemaValidator(*not_, false);
- if (hasSchemaDependencies_) {
- for (SizeType i = 0; i < propertyCount_; i++)
- if (properties_[i].dependenciesSchema)
- context.validators[properties_[i].dependenciesValidatorIndex] = context.factory.CreateSchemaValidator(*properties_[i].dependenciesSchema, false);
- }
- }
- // Add any other type-independent checks here
- if (readOnly_ && (context.flags & kValidateWriteFlag)) {
- context.error_handler.DisallowedWhenWriting();
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorReadOnly);
- }
- if (writeOnly_ && (context.flags & kValidateReadFlag)) {
- context.error_handler.DisallowedWhenReading();
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorWriteOnly);
- }
- return true;
- }
- void CreateSchemaValidators(Context& context, const SchemaArray& schemas, const bool inheritContinueOnErrors) const {
- for (SizeType i = 0; i < schemas.count; i++)
- context.validators[schemas.begin + i] = context.factory.CreateSchemaValidator(*schemas.schemas[i], inheritContinueOnErrors);
- }
- // O(n)
- bool FindPropertyIndex(const ValueType& name, SizeType* outIndex) const {
- SizeType len = name.GetStringLength();
- const Ch* str = name.GetString();
- for (SizeType index = 0; index < propertyCount_; index++)
- if (properties_[index].name.GetStringLength() == len &&
- (std::memcmp(properties_[index].name.GetString(), str, sizeof(Ch) * len) == 0))
- {
- *outIndex = index;
- return true;
- }
- return false;
- }
- bool CheckBool(Context& context, bool) const {
- if (!(type_ & (1 << kBooleanSchemaType))) {
- DisallowedType(context, GetBooleanString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- return true;
- }
- bool CheckInt(Context& context, int64_t i) const {
- if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
- DisallowedType(context, GetIntegerString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- if (!minimum_.IsNull()) {
- if (minimum_.IsInt64()) {
- if (exclusiveMinimum_ ? i <= minimum_.GetInt64() : i < minimum_.GetInt64()) {
- context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
- }
- }
- else if (minimum_.IsUint64()) {
- context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum); // i <= max(int64_t) < minimum.GetUint64()
- }
- else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
- return false;
- }
- if (!maximum_.IsNull()) {
- if (maximum_.IsInt64()) {
- if (exclusiveMaximum_ ? i >= maximum_.GetInt64() : i > maximum_.GetInt64()) {
- context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
- }
- }
- else if (maximum_.IsUint64()) { }
- /* do nothing */ // i <= max(int64_t) < maximum_.GetUint64()
- else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
- return false;
- }
- if (!multipleOf_.IsNull()) {
- if (multipleOf_.IsUint64()) {
- if (static_cast<uint64_t>(i >= 0 ? i : -i) % multipleOf_.GetUint64() != 0) {
- context.error_handler.NotMultipleOf(i, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
- }
- }
- else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
- return false;
- }
- return true;
- }
- bool CheckUint(Context& context, uint64_t i) const {
- if (!(type_ & ((1 << kIntegerSchemaType) | (1 << kNumberSchemaType)))) {
- DisallowedType(context, GetIntegerString());
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorType);
- }
- if (!minimum_.IsNull()) {
- if (minimum_.IsUint64()) {
- if (exclusiveMinimum_ ? i <= minimum_.GetUint64() : i < minimum_.GetUint64()) {
- context.error_handler.BelowMinimum(i, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
- }
- }
- else if (minimum_.IsInt64())
- /* do nothing */; // i >= 0 > minimum.Getint64()
- else if (!CheckDoubleMinimum(context, static_cast<double>(i)))
- return false;
- }
- if (!maximum_.IsNull()) {
- if (maximum_.IsUint64()) {
- if (exclusiveMaximum_ ? i >= maximum_.GetUint64() : i > maximum_.GetUint64()) {
- context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
- }
- }
- else if (maximum_.IsInt64()) {
- context.error_handler.AboveMaximum(i, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum); // i >= 0 > maximum_
- }
- else if (!CheckDoubleMaximum(context, static_cast<double>(i)))
- return false;
- }
- if (!multipleOf_.IsNull()) {
- if (multipleOf_.IsUint64()) {
- if (i % multipleOf_.GetUint64() != 0) {
- context.error_handler.NotMultipleOf(i, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
- }
- }
- else if (!CheckDoubleMultipleOf(context, static_cast<double>(i)))
- return false;
- }
- return true;
- }
- bool CheckDoubleMinimum(Context& context, double d) const {
- if (exclusiveMinimum_ ? d <= minimum_.GetDouble() : d < minimum_.GetDouble()) {
- context.error_handler.BelowMinimum(d, minimum_, exclusiveMinimum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMinimum_ ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum);
- }
- return true;
- }
- bool CheckDoubleMaximum(Context& context, double d) const {
- if (exclusiveMaximum_ ? d >= maximum_.GetDouble() : d > maximum_.GetDouble()) {
- context.error_handler.AboveMaximum(d, maximum_, exclusiveMaximum_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(exclusiveMaximum_ ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum);
- }
- return true;
- }
- bool CheckDoubleMultipleOf(Context& context, double d) const {
- double a = std::abs(d), b = std::abs(multipleOf_.GetDouble());
- double q = a / b;
- double qRounded = std::floor(q + 0.5);
- double scaledEpsilon = (q + qRounded) * std::numeric_limits<double>::epsilon();
- double difference = std::abs(qRounded - q);
- bool isMultiple = (difference <= scaledEpsilon)
- || (difference < std::numeric_limits<double>::min());
- if (!isMultiple) {
- context.error_handler.NotMultipleOf(d, multipleOf_);
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorMultipleOf);
- }
- return true;
- }
- void DisallowedType(Context& context, const ValueType& actualType) const {
- ErrorHandler& eh = context.error_handler;
- eh.StartDisallowedType();
- if (type_ & (1 << kNullSchemaType)) eh.AddExpectedType(GetNullString());
- if (type_ & (1 << kBooleanSchemaType)) eh.AddExpectedType(GetBooleanString());
- if (type_ & (1 << kObjectSchemaType)) eh.AddExpectedType(GetObjectString());
- if (type_ & (1 << kArraySchemaType)) eh.AddExpectedType(GetArrayString());
- if (type_ & (1 << kStringSchemaType)) eh.AddExpectedType(GetStringString());
- if (type_ & (1 << kNumberSchemaType)) eh.AddExpectedType(GetNumberString());
- else if (type_ & (1 << kIntegerSchemaType)) eh.AddExpectedType(GetIntegerString());
- eh.EndDisallowedType(actualType);
- }
- struct Property {
- Property() : schema(), dependenciesSchema(), dependenciesValidatorIndex(), dependencies(), required(false) {}
- ~Property() { AllocatorType::Free(dependencies); }
- SValue name;
- const SchemaType* schema;
- const SchemaType* dependenciesSchema;
- SizeType dependenciesValidatorIndex;
- bool* dependencies;
- bool required;
- };
- struct PatternProperty {
- PatternProperty() : schema(), pattern() {}
- ~PatternProperty() {
- if (pattern) {
- pattern->~RegexType();
- AllocatorType::Free(pattern);
- }
- }
- const SchemaType* schema;
- RegexType* pattern;
- };
- AllocatorType* allocator_;
- SValue uri_;
- UriType id_;
- Specification spec_;
- PointerType pointer_;
- const SchemaType* typeless_;
- uint64_t* enum_;
- SizeType enumCount_;
- SchemaArray allOf_;
- SchemaArray anyOf_;
- SchemaArray oneOf_;
- const SchemaType* not_;
- unsigned type_; // bitmask of kSchemaType
- SizeType validatorCount_;
- SizeType notValidatorIndex_;
- Property* properties_;
- const SchemaType* additionalPropertiesSchema_;
- PatternProperty* patternProperties_;
- SizeType patternPropertyCount_;
- SizeType propertyCount_;
- SizeType minProperties_;
- SizeType maxProperties_;
- bool additionalProperties_;
- bool hasDependencies_;
- bool hasRequired_;
- bool hasSchemaDependencies_;
- const SchemaType* additionalItemsSchema_;
- const SchemaType* itemsList_;
- const SchemaType** itemsTuple_;
- SizeType itemsTupleCount_;
- SizeType minItems_;
- SizeType maxItems_;
- bool additionalItems_;
- bool uniqueItems_;
- RegexType* pattern_;
- SizeType minLength_;
- SizeType maxLength_;
- SValue minimum_;
- SValue maximum_;
- SValue multipleOf_;
- bool exclusiveMinimum_;
- bool exclusiveMaximum_;
- SizeType defaultValueLength_;
- bool readOnly_;
- bool writeOnly_;
- bool nullable_;
- };
- template<typename Stack, typename Ch>
- struct TokenHelper {
- RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
- *documentStack.template Push<Ch>() = '/';
- char buffer[21];
- size_t length = static_cast<size_t>((sizeof(SizeType) == 4 ? u32toa(index, buffer) : u64toa(index, buffer)) - buffer);
- for (size_t i = 0; i < length; i++)
- *documentStack.template Push<Ch>() = static_cast<Ch>(buffer[i]);
- }
- };
- // Partial specialized version for char to prevent buffer copying.
- template <typename Stack>
- struct TokenHelper<Stack, char> {
- RAPIDJSON_FORCEINLINE static void AppendIndexToken(Stack& documentStack, SizeType index) {
- if (sizeof(SizeType) == 4) {
- char *buffer = documentStack.template Push<char>(1 + 10); // '/' + uint
- *buffer++ = '/';
- const char* end = internal::u32toa(index, buffer);
- documentStack.template Pop<char>(static_cast<size_t>(10 - (end - buffer)));
- }
- else {
- char *buffer = documentStack.template Push<char>(1 + 20); // '/' + uint64
- *buffer++ = '/';
- const char* end = internal::u64toa(index, buffer);
- documentStack.template Pop<char>(static_cast<size_t>(20 - (end - buffer)));
- }
- }
- };
- } // namespace internal
- ///////////////////////////////////////////////////////////////////////////////
- // IGenericRemoteSchemaDocumentProvider
- template <typename SchemaDocumentType>
- class IGenericRemoteSchemaDocumentProvider {
- public:
- typedef typename SchemaDocumentType::Ch Ch;
- typedef typename SchemaDocumentType::ValueType ValueType;
- typedef typename SchemaDocumentType::AllocatorType AllocatorType;
- virtual ~IGenericRemoteSchemaDocumentProvider() {}
- virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
- virtual const SchemaDocumentType* GetRemoteDocument(const GenericUri<ValueType, AllocatorType> uri, Specification& spec) {
- // Default implementation just calls through for compatibility
- // Following line suppresses unused parameter warning
- (void)spec;
- // printf("GetRemoteDocument: %d %d\n", spec.draft, spec.oapi);
- return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength());
- }
- };
- ///////////////////////////////////////////////////////////////////////////////
- // GenericSchemaDocument
- //! JSON schema document.
- /*!
- A JSON schema document is a compiled version of a JSON schema.
- It is basically a tree of internal::Schema.
- \note This is an immutable class (i.e. its instance cannot be modified after construction).
- \tparam ValueT Type of JSON value (e.g. \c Value ), which also determine the encoding.
- \tparam Allocator Allocator type for allocating memory of this document.
- */
- template <typename ValueT, typename Allocator = CrtAllocator>
- class GenericSchemaDocument {
- public:
- typedef ValueT ValueType;
- typedef IGenericRemoteSchemaDocumentProvider<GenericSchemaDocument> IRemoteSchemaDocumentProviderType;
- typedef Allocator AllocatorType;
- typedef typename ValueType::EncodingType EncodingType;
- typedef typename EncodingType::Ch Ch;
- typedef internal::Schema<GenericSchemaDocument> SchemaType;
- typedef GenericPointer<ValueType, Allocator> PointerType;
- typedef GenericValue<EncodingType, AllocatorType> GValue;
- typedef GenericUri<ValueType, Allocator> UriType;
- typedef GenericStringRef<Ch> StringRefType;
- friend class internal::Schema<GenericSchemaDocument>;
- template <typename, typename, typename>
- friend class GenericSchemaValidator;
- //! Constructor.
- /*!
- Compile a JSON document into schema document.
- \param document A JSON document as source.
- \param uri The base URI of this schema document for purposes of violation reporting.
- \param uriLength Length of \c name, in code points.
- \param remoteProvider An optional remote schema document provider for resolving remote reference. Can be null.
- \param allocator An optional allocator instance for allocating memory. Can be null.
- \param pointer An optional JSON pointer to the start of the schema document
- \param spec Optional schema draft or OpenAPI version. Used if no specification in document. Defaults to draft-04.
- */
- explicit GenericSchemaDocument(const ValueType& document, const Ch* uri = 0, SizeType uriLength = 0,
- IRemoteSchemaDocumentProviderType* remoteProvider = 0, Allocator* allocator = 0,
- const PointerType& pointer = PointerType(), // PR #1393
- const Specification& spec = Specification(kDraft04)) :
- remoteProvider_(remoteProvider),
- allocator_(allocator),
- ownAllocator_(),
- root_(),
- typeless_(),
- schemaMap_(allocator, kInitialSchemaMapSize),
- schemaRef_(allocator, kInitialSchemaRefSize),
- spec_(spec),
- error_(kObjectType),
- currentError_()
- {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::GenericSchemaDocument");
- if (!allocator_)
- ownAllocator_ = allocator_ = RAPIDJSON_NEW(Allocator)();
- Ch noUri[1] = {0};
- uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
- docId_ = UriType(uri_, allocator_);
- typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
- new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
- // Establish the schema draft or open api version.
- // We only ever look for '$schema' or 'swagger' or 'openapi' at the root of the document.
- SetSchemaSpecification(document);
- // Generate root schema, it will call CreateSchema() to create sub-schemas,
- // And call HandleRefSchema() if there are $ref.
- // PR #1393 use input pointer if supplied
- root_ = typeless_;
- if (pointer.GetTokenCount() == 0) {
- CreateSchemaRecursive(&root_, pointer, document, document, docId_);
- }
- else if (const ValueType* v = pointer.Get(document)) {
- CreateSchema(&root_, pointer, *v, document, docId_);
- }
- else {
- GenericStringBuffer<EncodingType> sb;
- pointer.StringifyUriFragment(sb);
- SchemaErrorValue(kSchemaErrorStartUnknown, PointerType(), sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)));
- }
- RAPIDJSON_ASSERT(root_ != 0);
- schemaRef_.ShrinkToFit(); // Deallocate all memory for ref
- }
- #if RAPIDJSON_HAS_CXX11_RVALUE_REFS
- //! Move constructor in C++11
- GenericSchemaDocument(GenericSchemaDocument&& rhs) RAPIDJSON_NOEXCEPT :
- remoteProvider_(rhs.remoteProvider_),
- allocator_(rhs.allocator_),
- ownAllocator_(rhs.ownAllocator_),
- root_(rhs.root_),
- typeless_(rhs.typeless_),
- schemaMap_(std::move(rhs.schemaMap_)),
- schemaRef_(std::move(rhs.schemaRef_)),
- uri_(std::move(rhs.uri_)),
- docId_(std::move(rhs.docId_)),
- spec_(rhs.spec_),
- error_(std::move(rhs.error_)),
- currentError_(std::move(rhs.currentError_))
- {
- rhs.remoteProvider_ = 0;
- rhs.allocator_ = 0;
- rhs.ownAllocator_ = 0;
- rhs.typeless_ = 0;
- }
- #endif
- //! Destructor
- ~GenericSchemaDocument() {
- while (!schemaMap_.Empty())
- schemaMap_.template Pop<SchemaEntry>(1)->~SchemaEntry();
- if (typeless_) {
- typeless_->~SchemaType();
- Allocator::Free(typeless_);
- }
- // these may contain some allocator data so clear before deleting ownAllocator_
- uri_.SetNull();
- error_.SetNull();
- currentError_.SetNull();
- RAPIDJSON_DELETE(ownAllocator_);
- }
- const GValue& GetURI() const { return uri_; }
- const Specification& GetSpecification() const { return spec_; }
- bool IsSupportedSpecification() const { return spec_.IsSupported(); }
- //! Static method to get the specification of any schema document
- // Returns kDraftNone if document is silent
- static const Specification GetSpecification(const ValueType& document) {
- SchemaDraft draft = GetSchemaDraft(document);
- if (draft != kDraftNone)
- return Specification(draft);
- else {
- OpenApiVersion oapi = GetOpenApiVersion(document);
- if (oapi != kVersionNone)
- return Specification(oapi);
- }
- return Specification(kDraftNone);
- }
- //! Get the root schema.
- const SchemaType& GetRoot() const { return *root_; }
- //! Gets the error object.
- GValue& GetError() { return error_; }
- const GValue& GetError() const { return error_; }
- static const StringRefType& GetSchemaErrorKeyword(SchemaErrorCode schemaErrorCode) {
- switch (schemaErrorCode) {
- case kSchemaErrorStartUnknown: return GetStartUnknownString();
- case kSchemaErrorRefPlainName: return GetRefPlainNameString();
- case kSchemaErrorRefInvalid: return GetRefInvalidString();
- case kSchemaErrorRefPointerInvalid: return GetRefPointerInvalidString();
- case kSchemaErrorRefUnknown: return GetRefUnknownString();
- case kSchemaErrorRefCyclical: return GetRefCyclicalString();
- case kSchemaErrorRefNoRemoteProvider: return GetRefNoRemoteProviderString();
- case kSchemaErrorRefNoRemoteSchema: return GetRefNoRemoteSchemaString();
- case kSchemaErrorRegexInvalid: return GetRegexInvalidString();
- case kSchemaErrorSpecUnknown: return GetSpecUnknownString();
- case kSchemaErrorSpecUnsupported: return GetSpecUnsupportedString();
- case kSchemaErrorSpecIllegal: return GetSpecIllegalString();
- case kSchemaErrorReadOnlyAndWriteOnly: return GetReadOnlyAndWriteOnlyString();
- default: return GetNullString();
- }
- }
- //! Default error method
- void SchemaError(const SchemaErrorCode code, const PointerType& location) {
- currentError_ = GValue(kObjectType);
- AddCurrentError(code, location);
- }
- //! Method for error with single string value insert
- void SchemaErrorValue(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length) {
- currentError_ = GValue(kObjectType);
- currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
- AddCurrentError(code, location);
- }
- //! Method for error with invalid pointer
- void SchemaErrorPointer(const SchemaErrorCode code, const PointerType& location, const Ch* value, SizeType length, const PointerType& pointer) {
- currentError_ = GValue(kObjectType);
- currentError_.AddMember(GetValueString(), GValue(value, length, *allocator_).Move(), *allocator_);
- currentError_.AddMember(GetOffsetString(), static_cast<SizeType>(pointer.GetParseErrorOffset() / sizeof(Ch)), *allocator_);
- AddCurrentError(code, location);
- }
- private:
- //! Prohibit copying
- GenericSchemaDocument(const GenericSchemaDocument&);
- //! Prohibit assignment
- GenericSchemaDocument& operator=(const GenericSchemaDocument&);
- typedef const PointerType* SchemaRefPtr; // PR #1393
- struct SchemaEntry {
- SchemaEntry(const PointerType& p, SchemaType* s, bool o, Allocator* allocator) : pointer(p, allocator), schema(s), owned(o) {}
- ~SchemaEntry() {
- if (owned) {
- schema->~SchemaType();
- Allocator::Free(schema);
- }
- }
- PointerType pointer;
- SchemaType* schema;
- bool owned;
- };
- void AddErrorInstanceLocation(GValue& result, const PointerType& location) {
- GenericStringBuffer<EncodingType> sb;
- location.StringifyUriFragment(sb);
- GValue instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)), *allocator_);
- result.AddMember(GetInstanceRefString(), instanceRef, *allocator_);
- }
- void AddError(GValue& keyword, GValue& error) {
- typename GValue::MemberIterator member = error_.FindMember(keyword);
- if (member == error_.MemberEnd())
- error_.AddMember(keyword, error, *allocator_);
- else {
- if (member->value.IsObject()) {
- GValue errors(kArrayType);
- errors.PushBack(member->value, *allocator_);
- member->value = errors;
- }
- member->value.PushBack(error, *allocator_);
- }
- }
- void AddCurrentError(const SchemaErrorCode code, const PointerType& location) {
- RAPIDJSON_SCHEMA_PRINT(InvalidKeyword, GetSchemaErrorKeyword(code));
- currentError_.AddMember(GetErrorCodeString(), code, *allocator_);
- AddErrorInstanceLocation(currentError_, location);
- AddError(GValue(GetSchemaErrorKeyword(code)).Move(), currentError_);
- }
- #define RAPIDJSON_STRING_(name, ...) \
- static const StringRefType& Get##name##String() {\
- static const Ch s[] = { __VA_ARGS__, '\0' };\
- static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
- return v;\
- }
- RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
- RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
- RAPIDJSON_STRING_(Value, 'v', 'a', 'l', 'u', 'e')
- RAPIDJSON_STRING_(Offset, 'o', 'f', 'f', 's', 'e', 't')
- RAPIDJSON_STRING_(Null, 'n', 'u', 'l', 'l')
- RAPIDJSON_STRING_(SpecUnknown, 'S', 'p', 'e', 'c', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
- RAPIDJSON_STRING_(SpecUnsupported, 'S', 'p', 'e', 'c', 'U', 'n', 's', 'u', 'p', 'p', 'o', 'r', 't', 'e', 'd')
- RAPIDJSON_STRING_(SpecIllegal, 'S', 'p', 'e', 'c', 'I', 'l', 'l', 'e', 'g', 'a', 'l')
- RAPIDJSON_STRING_(StartUnknown, 'S', 't', 'a', 'r', 't', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
- RAPIDJSON_STRING_(RefPlainName, 'R', 'e', 'f', 'P', 'l', 'a', 'i', 'n', 'N', 'a', 'm', 'e')
- RAPIDJSON_STRING_(RefInvalid, 'R', 'e', 'f', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
- RAPIDJSON_STRING_(RefPointerInvalid, 'R', 'e', 'f', 'P', 'o', 'i', 'n', 't', 'e', 'r', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
- RAPIDJSON_STRING_(RefUnknown, 'R', 'e', 'f', 'U', 'n', 'k', 'n', 'o', 'w', 'n')
- RAPIDJSON_STRING_(RefCyclical, 'R', 'e', 'f', 'C', 'y', 'c', 'l', 'i', 'c', 'a', 'l')
- RAPIDJSON_STRING_(RefNoRemoteProvider, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'P', 'r', 'o', 'v', 'i', 'd', 'e', 'r')
- RAPIDJSON_STRING_(RefNoRemoteSchema, 'R', 'e', 'f', 'N', 'o', 'R', 'e', 'm', 'o', 't', 'e', 'S', 'c', 'h', 'e', 'm', 'a')
- RAPIDJSON_STRING_(ReadOnlyAndWriteOnly, 'R', 'e', 'a', 'd', 'O', 'n', 'l', 'y', 'A', 'n', 'd', 'W', 'r', 'i', 't', 'e', 'O', 'n', 'l', 'y')
- RAPIDJSON_STRING_(RegexInvalid, 'R', 'e', 'g', 'e', 'x', 'I', 'n', 'v', 'a', 'l', 'i', 'd')
- #undef RAPIDJSON_STRING_
- // Static method to get schema draft of any schema document
- static SchemaDraft GetSchemaDraft(const ValueType& document) {
- static const Ch kDraft03String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '3', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
- static const Ch kDraft04String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '4', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
- static const Ch kDraft05String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '5', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
- static const Ch kDraft06String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '6', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
- static const Ch kDraft07String[] = { 'h', 't', 't', 'p', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '-', '0', '7', '/', 's', 'c', 'h', 'e', 'm', 'a', '#', '\0' };
- static const Ch kDraft2019_09String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '1', '9', '-', '0', '9', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
- static const Ch kDraft2020_12String[] = { 'h', 't', 't', 'p', 's', ':', '/', '/', 'j', 's', 'o', 'n', '-', 's', 'c', 'h', 'e', 'm', 'a', '.', 'o', 'r', 'g', '/', 'd', 'r', 'a', 'f', 't', '/', '2', '0', '2', '0', '-', '1', '2', '/', 's', 'c', 'h', 'e', 'm', 'a', '\0' };
- if (!document.IsObject()) {
- return kDraftNone;
- }
- // Get the schema draft from the $schema keyword at the supplied location
- typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSchemaString());
- if (itr != document.MemberEnd()) {
- if (!itr->value.IsString()) return kDraftUnknown;
- const UriType draftUri(itr->value);
- // Check base uri for match
- if (draftUri.Match(UriType(kDraft04String), false)) return kDraft04;
- if (draftUri.Match(UriType(kDraft05String), false)) return kDraft05;
- if (draftUri.Match(UriType(kDraft06String), false)) return kDraft06;
- if (draftUri.Match(UriType(kDraft07String), false)) return kDraft07;
- if (draftUri.Match(UriType(kDraft03String), false)) return kDraft03;
- if (draftUri.Match(UriType(kDraft2019_09String), false)) return kDraft2019_09;
- if (draftUri.Match(UriType(kDraft2020_12String), false)) return kDraft2020_12;
- return kDraftUnknown;
- }
- // $schema not found
- return kDraftNone;
- }
- // Get open api version of any schema document
- static OpenApiVersion GetOpenApiVersion(const ValueType& document) {
- static const Ch kVersion20String[] = { '2', '.', '0', '\0' };
- static const Ch kVersion30String[] = { '3', '.', '0', '.', '\0' }; // ignore patch level
- static const Ch kVersion31String[] = { '3', '.', '1', '.', '\0' }; // ignore patch level
- static SizeType len = internal::StrLen<Ch>(kVersion30String);
- if (!document.IsObject()) {
- return kVersionNone;
- }
- // Get the open api version from the swagger / openapi keyword at the supplied location
- typename ValueType::ConstMemberIterator itr = document.FindMember(SchemaType::GetSwaggerString());
- if (itr == document.MemberEnd()) itr = document.FindMember(SchemaType::GetOpenApiString());
- if (itr != document.MemberEnd()) {
- if (!itr->value.IsString()) return kVersionUnknown;
- const ValueType kVersion20Value(kVersion20String);
- if (kVersion20Value == itr->value) return kVersion20; // must match 2.0 exactly
- const ValueType kVersion30Value(kVersion30String);
- if (itr->value.GetStringLength() > len && kVersion30Value == ValueType(itr->value.GetString(), len)) return kVersion30; // must match 3.0.x
- const ValueType kVersion31Value(kVersion31String);
- if (itr->value.GetStringLength() > len && kVersion31Value == ValueType(itr->value.GetString(), len)) return kVersion31; // must match 3.1.x
- return kVersionUnknown;
- }
- // swagger or openapi not found
- return kVersionNone;
- }
- // Get the draft of the schema or the open api version (which implies the draft).
- // Report an error if schema draft or open api version not supported or not recognized, or both in document, and carry on.
- void SetSchemaSpecification(const ValueType& document) {
- // Look for '$schema', 'swagger' or 'openapi' keyword at document root
- SchemaDraft docDraft = GetSchemaDraft(document);
- OpenApiVersion docOapi = GetOpenApiVersion(document);
- // Error if both in document
- if (docDraft != kDraftNone && docOapi != kVersionNone)
- SchemaError(kSchemaErrorSpecIllegal, PointerType());
- // Use document draft or open api version if present or use spec from constructor
- if (docDraft != kDraftNone)
- spec_ = Specification(docDraft);
- else if (docOapi != kVersionNone)
- spec_ = Specification(docOapi);
- // Error if draft or version unknown
- if (spec_.draft == kDraftUnknown || spec_.oapi == kVersionUnknown)
- SchemaError(kSchemaErrorSpecUnknown, PointerType());
- else if (!spec_.IsSupported())
- SchemaError(kSchemaErrorSpecUnsupported, PointerType());
- }
- // Changed by PR #1393
- void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
- if (v.GetType() == kObjectType) {
- UriType newid = UriType(CreateSchema(schema, pointer, v, document, id), allocator_);
- for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
- CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
- }
- else if (v.GetType() == kArrayType)
- for (SizeType i = 0; i < v.Size(); i++)
- CreateSchemaRecursive(0, pointer.Append(i, allocator_), v[i], document, id);
- }
- // Changed by PR #1393
- const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
- RAPIDJSON_ASSERT(pointer.IsValid());
- GenericStringBuffer<EncodingType> sb;
- pointer.StringifyUriFragment(sb);
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::CreateSchema", sb.GetString(), id.GetString());
- if (v.IsObject()) {
- if (const SchemaType* sc = GetSchema(pointer)) {
- if (schema)
- *schema = sc;
- AddSchemaRefs(const_cast<SchemaType*>(sc));
- }
- else if (!HandleRefSchema(pointer, schema, v, document, id)) {
- // The new schema constructor adds itself and its $ref(s) to schemaMap_
- SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
- if (schema)
- *schema = s;
- return s->GetId();
- }
- }
- else {
- if (schema)
- *schema = typeless_;
- AddSchemaRefs(typeless_);
- }
- return id;
- }
- // Changed by PR #1393
- // TODO should this return a UriType& ?
- bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
- typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
- if (itr == v.MemberEnd())
- return false;
- GenericStringBuffer<EncodingType> sb;
- source.StringifyUriFragment(sb);
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::HandleRefSchema", sb.GetString(), id.GetString());
- // Resolve the source pointer to the $ref'ed schema (finally)
- new (schemaRef_.template Push<SchemaRefPtr>()) SchemaRefPtr(&source);
- if (itr->value.IsString()) {
- SizeType len = itr->value.GetStringLength();
- if (len == 0)
- SchemaError(kSchemaErrorRefInvalid, source);
- else {
- // First resolve $ref against the in-scope id
- UriType scopeId = UriType(id, allocator_);
- UriType ref = UriType(itr->value, allocator_).Resolve(scopeId, allocator_);
- RAPIDJSON_SCHEMA_PRINT(SchemaIds, id.GetString(), itr->value.GetString(), ref.GetString());
- // See if the resolved $ref minus the fragment matches a resolved id in this document
- // Search from the root. Returns the subschema in the document and its absolute JSON pointer.
- PointerType basePointer = PointerType();
- const ValueType *base = FindId(document, ref, basePointer, docId_, false);
- if (!base) {
- // Remote reference - call the remote document provider
- if (!remoteProvider_)
- SchemaError(kSchemaErrorRefNoRemoteProvider, source);
- else {
- if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref, spec_)) {
- const Ch* s = ref.GetFragString();
- len = ref.GetFragStringLength();
- if (len <= 1 || s[1] == '/') {
- // JSON pointer fragment, absolute in the remote schema
- const PointerType pointer(s, len, allocator_);
- if (!pointer.IsValid())
- SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, pointer);
- else {
- // Get the subschema
- if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
- if (schema)
- *schema = sc;
- AddSchemaRefs(const_cast<SchemaType *>(sc));
- return true;
- } else
- SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
- }
- } else
- // Plain name fragment, not allowed in remote schema
- SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
- } else
- SchemaErrorValue(kSchemaErrorRefNoRemoteSchema, source, ref.GetString(), ref.GetStringLength());
- }
- }
- else { // Local reference
- const Ch* s = ref.GetFragString();
- len = ref.GetFragStringLength();
- if (len <= 1 || s[1] == '/') {
- // JSON pointer fragment, relative to the resolved URI
- const PointerType relPointer(s, len, allocator_);
- if (!relPointer.IsValid())
- SchemaErrorPointer(kSchemaErrorRefPointerInvalid, source, s, len, relPointer);
- else {
- // Get the subschema
- if (const ValueType *pv = relPointer.Get(*base)) {
- // Now get the absolute JSON pointer by adding relative to base
- PointerType pointer(basePointer, allocator_);
- for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
- pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
- if (IsCyclicRef(pointer))
- SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
- else {
- // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
- // TODO: cache pointer <-> id mapping
- size_t unresolvedTokenIndex;
- scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
- CreateSchema(schema, pointer, *pv, document, scopeId);
- return true;
- }
- } else
- SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
- }
- } else {
- // Plain name fragment, relative to the resolved URI
- // Not supported in open api 2.0 and 3.0
- PointerType pointer(allocator_);
- if (spec_.oapi == kVersion20 || spec_.oapi == kVersion30)
- SchemaErrorValue(kSchemaErrorRefPlainName, source, s, len);
- // See if the fragment matches an id in this document.
- // Search from the base we just established. Returns the subschema in the document and its absolute JSON pointer.
- else if (const ValueType *pv = FindId(*base, ref, pointer, UriType(ref.GetBaseString(), ref.GetBaseStringLength(), allocator_), true, basePointer)) {
- if (IsCyclicRef(pointer))
- SchemaErrorValue(kSchemaErrorRefCyclical, source, ref.GetString(), ref.GetStringLength());
- else {
- // Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
- // TODO: cache pointer <-> id mapping
- size_t unresolvedTokenIndex;
- scopeId = pointer.GetUri(document, docId_, &unresolvedTokenIndex, allocator_);
- CreateSchema(schema, pointer, *pv, document, scopeId);
- return true;
- }
- } else
- SchemaErrorValue(kSchemaErrorRefUnknown, source, ref.GetString(), ref.GetStringLength());
- }
- }
- }
- }
- // Invalid/Unknown $ref
- if (schema)
- *schema = typeless_;
- AddSchemaRefs(typeless_);
- return true;
- }
- //! Find the first subschema with a resolved 'id' that matches the specified URI.
- // If full specified use all URI else ignore fragment.
- // If found, return a pointer to the subschema and its JSON pointer.
- // TODO cache pointer <-> id mapping
- ValueType* FindId(const ValueType& doc, const UriType& finduri, PointerType& resptr, const UriType& baseuri, bool full, const PointerType& here = PointerType()) const {
- SizeType i = 0;
- ValueType* resval = 0;
- UriType tempuri = UriType(finduri, allocator_);
- UriType localuri = UriType(baseuri, allocator_);
- if (doc.GetType() == kObjectType) {
- // Establish the base URI of this object
- typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
- if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
- localuri = UriType(m->value, allocator_).Resolve(baseuri, allocator_);
- }
- // See if it matches
- if (localuri.Match(finduri, full)) {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::FindId (match)", full ? localuri.GetString() : localuri.GetBaseString());
- resval = const_cast<ValueType *>(&doc);
- resptr = here;
- return resval;
- }
- // No match, continue looking
- for (m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
- if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
- resval = FindId(m->value, finduri, resptr, localuri, full, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
- }
- if (resval) break;
- }
- } else if (doc.GetType() == kArrayType) {
- // Continue looking
- for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
- if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
- resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
- }
- if (resval) break;
- i++;
- }
- }
- return resval;
- }
- // Added by PR #1393
- void AddSchemaRefs(SchemaType* schema) {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaDocument::AddSchemaRefs");
- while (!schemaRef_.Empty()) {
- SchemaRefPtr *ref = schemaRef_.template Pop<SchemaRefPtr>(1);
- SchemaEntry *entry = schemaMap_.template Push<SchemaEntry>();
- new (entry) SchemaEntry(**ref, schema, false, allocator_);
- }
- }
- // Added by PR #1393
- bool IsCyclicRef(const PointerType& pointer) const {
- for (const SchemaRefPtr* ref = schemaRef_.template Bottom<SchemaRefPtr>(); ref != schemaRef_.template End<SchemaRefPtr>(); ++ref)
- if (pointer == **ref)
- return true;
- return false;
- }
- const SchemaType* GetSchema(const PointerType& pointer) const {
- for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
- if (pointer == target->pointer)
- return target->schema;
- return 0;
- }
- PointerType GetPointer(const SchemaType* schema) const {
- for (const SchemaEntry* target = schemaMap_.template Bottom<SchemaEntry>(); target != schemaMap_.template End<SchemaEntry>(); ++target)
- if (schema == target->schema)
- return target->pointer;
- return PointerType();
- }
- const SchemaType* GetTypeless() const { return typeless_; }
- static const size_t kInitialSchemaMapSize = 64;
- static const size_t kInitialSchemaRefSize = 64;
- IRemoteSchemaDocumentProviderType* remoteProvider_;
- Allocator *allocator_;
- Allocator *ownAllocator_;
- const SchemaType* root_; //!< Root schema.
- SchemaType* typeless_;
- internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
- internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
- GValue uri_; // Schema document URI
- UriType docId_;
- Specification spec_;
- GValue error_;
- GValue currentError_;
- };
- //! GenericSchemaDocument using Value type.
- typedef GenericSchemaDocument<Value> SchemaDocument;
- //! IGenericRemoteSchemaDocumentProvider using SchemaDocument.
- typedef IGenericRemoteSchemaDocumentProvider<SchemaDocument> IRemoteSchemaDocumentProvider;
- ///////////////////////////////////////////////////////////////////////////////
- // GenericSchemaValidator
- //! JSON Schema Validator.
- /*!
- A SAX style JSON schema validator.
- It uses a \c GenericSchemaDocument to validate SAX events.
- It delegates the incoming SAX events to an output handler.
- The default output handler does nothing.
- It can be reused multiple times by calling \c Reset().
- \tparam SchemaDocumentType Type of schema document.
- \tparam OutputHandler Type of output handler. Default handler does nothing.
- \tparam StateAllocator Allocator for storing the internal validation states.
- */
- template <
- typename SchemaDocumentType,
- typename OutputHandler = BaseReaderHandler<typename SchemaDocumentType::SchemaType::EncodingType>,
- typename StateAllocator = CrtAllocator>
- class GenericSchemaValidator :
- public internal::ISchemaStateFactory<typename SchemaDocumentType::SchemaType>,
- public internal::ISchemaValidator,
- public internal::IValidationErrorHandler<typename SchemaDocumentType::SchemaType> {
- public:
- typedef typename SchemaDocumentType::SchemaType SchemaType;
- typedef typename SchemaDocumentType::PointerType PointerType;
- typedef typename SchemaType::EncodingType EncodingType;
- typedef typename SchemaType::SValue SValue;
- typedef typename EncodingType::Ch Ch;
- typedef GenericStringRef<Ch> StringRefType;
- typedef GenericValue<EncodingType, StateAllocator> ValueType;
- //! Constructor without output handler.
- /*!
- \param schemaDocument The schema document to conform to.
- \param allocator Optional allocator for storing internal validation states.
- \param schemaStackCapacity Optional initial capacity of schema path stack.
- \param documentStackCapacity Optional initial capacity of document path stack.
- */
- GenericSchemaValidator(
- const SchemaDocumentType& schemaDocument,
- StateAllocator* allocator = 0,
- size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
- size_t documentStackCapacity = kDefaultDocumentStackCapacity)
- :
- schemaDocument_(&schemaDocument),
- root_(schemaDocument.GetRoot()),
- stateAllocator_(allocator),
- ownStateAllocator_(0),
- schemaStack_(allocator, schemaStackCapacity),
- documentStack_(allocator, documentStackCapacity),
- outputHandler_(0),
- error_(kObjectType),
- currentError_(),
- missingDependents_(),
- valid_(true),
- flags_(kValidateDefaultFlags),
- depth_(0)
- {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator");
- }
- //! Constructor with output handler.
- /*!
- \param schemaDocument The schema document to conform to.
- \param allocator Optional allocator for storing internal validation states.
- \param schemaStackCapacity Optional initial capacity of schema path stack.
- \param documentStackCapacity Optional initial capacity of document path stack.
- */
- GenericSchemaValidator(
- const SchemaDocumentType& schemaDocument,
- OutputHandler& outputHandler,
- StateAllocator* allocator = 0,
- size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
- size_t documentStackCapacity = kDefaultDocumentStackCapacity)
- :
- schemaDocument_(&schemaDocument),
- root_(schemaDocument.GetRoot()),
- stateAllocator_(allocator),
- ownStateAllocator_(0),
- schemaStack_(allocator, schemaStackCapacity),
- documentStack_(allocator, documentStackCapacity),
- outputHandler_(&outputHandler),
- error_(kObjectType),
- currentError_(),
- missingDependents_(),
- valid_(true),
- flags_(kValidateDefaultFlags),
- depth_(0)
- {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (output handler)");
- }
- //! Destructor.
- ~GenericSchemaValidator() {
- Reset();
- RAPIDJSON_DELETE(ownStateAllocator_);
- }
- //! Reset the internal states.
- void Reset() {
- while (!schemaStack_.Empty())
- PopSchema();
- documentStack_.Clear();
- ResetError();
- }
- //! Reset the error state.
- void ResetError() {
- error_.SetObject();
- currentError_.SetNull();
- missingDependents_.SetNull();
- valid_ = true;
- }
- //! Implementation of ISchemaValidator
- void SetValidateFlags(unsigned flags) {
- flags_ = flags;
- }
- virtual unsigned GetValidateFlags() const {
- return flags_;
- }
- virtual bool IsValid() const {
- if (!valid_) return false;
- if (GetContinueOnErrors() && !error_.ObjectEmpty()) return false;
- return true;
- }
- //! End of Implementation of ISchemaValidator
- //! Gets the error object.
- ValueType& GetError() { return error_; }
- const ValueType& GetError() const { return error_; }
- //! Gets the JSON pointer pointed to the invalid schema.
- // If reporting all errors, the stack will be empty.
- PointerType GetInvalidSchemaPointer() const {
- return schemaStack_.Empty() ? PointerType() : CurrentSchema().GetPointer();
- }
- //! Gets the keyword of invalid schema.
- // If reporting all errors, the stack will be empty, so return "errors".
- const Ch* GetInvalidSchemaKeyword() const {
- if (!schemaStack_.Empty()) return CurrentContext().invalidKeyword;
- if (GetContinueOnErrors() && !error_.ObjectEmpty()) return static_cast<const Ch*>(GetErrorsString());
- return 0;
- }
- //! Gets the error code of invalid schema.
- // If reporting all errors, the stack will be empty, so return kValidateErrors.
- ValidateErrorCode GetInvalidSchemaCode() const {
- if (!schemaStack_.Empty()) return CurrentContext().invalidCode;
- if (GetContinueOnErrors() && !error_.ObjectEmpty()) return kValidateErrors;
- return kValidateErrorNone;
- }
- //! Gets the JSON pointer pointed to the invalid value.
- // If reporting all errors, the stack will be empty.
- PointerType GetInvalidDocumentPointer() const {
- if (documentStack_.Empty()) {
- return PointerType();
- }
- else {
- return PointerType(documentStack_.template Bottom<Ch>(), documentStack_.GetSize() / sizeof(Ch));
- }
- }
- void NotMultipleOf(int64_t actual, const SValue& expected) {
- AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
- }
- void NotMultipleOf(uint64_t actual, const SValue& expected) {
- AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
- }
- void NotMultipleOf(double actual, const SValue& expected) {
- AddNumberError(kValidateErrorMultipleOf, ValueType(actual).Move(), expected);
- }
- void AboveMaximum(int64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
- }
- void AboveMaximum(uint64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
- }
- void AboveMaximum(double actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMaximum : kValidateErrorMaximum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMaximumString : 0);
- }
- void BelowMinimum(int64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
- }
- void BelowMinimum(uint64_t actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
- }
- void BelowMinimum(double actual, const SValue& expected, bool exclusive) {
- AddNumberError(exclusive ? kValidateErrorExclusiveMinimum : kValidateErrorMinimum, ValueType(actual).Move(), expected,
- exclusive ? &SchemaType::GetExclusiveMinimumString : 0);
- }
- void TooLong(const Ch* str, SizeType length, SizeType expected) {
- AddNumberError(kValidateErrorMaxLength,
- ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
- }
- void TooShort(const Ch* str, SizeType length, SizeType expected) {
- AddNumberError(kValidateErrorMinLength,
- ValueType(str, length, GetStateAllocator()).Move(), SValue(expected).Move());
- }
- void DoesNotMatch(const Ch* str, SizeType length) {
- currentError_.SetObject();
- currentError_.AddMember(GetActualString(), ValueType(str, length, GetStateAllocator()).Move(), GetStateAllocator());
- AddCurrentError(kValidateErrorPattern);
- }
- void DisallowedItem(SizeType index) {
- currentError_.SetObject();
- currentError_.AddMember(GetDisallowedString(), ValueType(index).Move(), GetStateAllocator());
- AddCurrentError(kValidateErrorAdditionalItems, true);
- }
- void TooFewItems(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(kValidateErrorMinItems,
- ValueType(actualCount).Move(), SValue(expectedCount).Move());
- }
- void TooManyItems(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(kValidateErrorMaxItems,
- ValueType(actualCount).Move(), SValue(expectedCount).Move());
- }
- void DuplicateItems(SizeType index1, SizeType index2) {
- ValueType duplicates(kArrayType);
- duplicates.PushBack(index1, GetStateAllocator());
- duplicates.PushBack(index2, GetStateAllocator());
- currentError_.SetObject();
- currentError_.AddMember(GetDuplicatesString(), duplicates, GetStateAllocator());
- AddCurrentError(kValidateErrorUniqueItems, true);
- }
- void TooManyProperties(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(kValidateErrorMaxProperties,
- ValueType(actualCount).Move(), SValue(expectedCount).Move());
- }
- void TooFewProperties(SizeType actualCount, SizeType expectedCount) {
- AddNumberError(kValidateErrorMinProperties,
- ValueType(actualCount).Move(), SValue(expectedCount).Move());
- }
- void StartMissingProperties() {
- currentError_.SetArray();
- }
- void AddMissingProperty(const SValue& name) {
- currentError_.PushBack(ValueType(name, GetStateAllocator()).Move(), GetStateAllocator());
- }
- bool EndMissingProperties() {
- if (currentError_.Empty())
- return false;
- ValueType error(kObjectType);
- error.AddMember(GetMissingString(), currentError_, GetStateAllocator());
- currentError_ = error;
- AddCurrentError(kValidateErrorRequired);
- return true;
- }
- void PropertyViolations(ISchemaValidator** subvalidators, SizeType count) {
- for (SizeType i = 0; i < count; ++i)
- MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
- }
- void DisallowedProperty(const Ch* name, SizeType length) {
- currentError_.SetObject();
- currentError_.AddMember(GetDisallowedString(), ValueType(name, length, GetStateAllocator()).Move(), GetStateAllocator());
- AddCurrentError(kValidateErrorAdditionalProperties, true);
- }
- void StartDependencyErrors() {
- currentError_.SetObject();
- }
- void StartMissingDependentProperties() {
- missingDependents_.SetArray();
- }
- void AddMissingDependentProperty(const SValue& targetName) {
- missingDependents_.PushBack(ValueType(targetName, GetStateAllocator()).Move(), GetStateAllocator());
- }
- void EndMissingDependentProperties(const SValue& sourceName) {
- if (!missingDependents_.Empty()) {
- // Create equivalent 'required' error
- ValueType error(kObjectType);
- ValidateErrorCode code = kValidateErrorRequired;
- error.AddMember(GetMissingString(), missingDependents_.Move(), GetStateAllocator());
- AddErrorCode(error, code);
- AddErrorInstanceLocation(error, false);
- // When appending to a pointer ensure its allocator is used
- PointerType schemaRef = GetInvalidSchemaPointer().Append(SchemaType::GetValidateErrorKeyword(kValidateErrorDependencies), &GetInvalidSchemaPointer().GetAllocator());
- AddErrorSchemaLocation(error, schemaRef.Append(sourceName.GetString(), sourceName.GetStringLength(), &GetInvalidSchemaPointer().GetAllocator()));
- ValueType wrapper(kObjectType);
- wrapper.AddMember(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator()).Move(), error, GetStateAllocator());
- currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(), wrapper, GetStateAllocator());
- }
- }
- void AddDependencySchemaError(const SValue& sourceName, ISchemaValidator* subvalidator) {
- currentError_.AddMember(ValueType(sourceName, GetStateAllocator()).Move(),
- static_cast<GenericSchemaValidator*>(subvalidator)->GetError(), GetStateAllocator());
- }
- bool EndDependencyErrors() {
- if (currentError_.ObjectEmpty())
- return false;
- ValueType error(kObjectType);
- error.AddMember(GetErrorsString(), currentError_, GetStateAllocator());
- currentError_ = error;
- AddCurrentError(kValidateErrorDependencies);
- return true;
- }
- void DisallowedValue(const ValidateErrorCode code = kValidateErrorEnum) {
- currentError_.SetObject();
- AddCurrentError(code);
- }
- void StartDisallowedType() {
- currentError_.SetArray();
- }
- void AddExpectedType(const typename SchemaType::ValueType& expectedType) {
- currentError_.PushBack(ValueType(expectedType, GetStateAllocator()).Move(), GetStateAllocator());
- }
- void EndDisallowedType(const typename SchemaType::ValueType& actualType) {
- ValueType error(kObjectType);
- error.AddMember(GetExpectedString(), currentError_, GetStateAllocator());
- error.AddMember(GetActualString(), ValueType(actualType, GetStateAllocator()).Move(), GetStateAllocator());
- currentError_ = error;
- AddCurrentError(kValidateErrorType);
- }
- void NotAllOf(ISchemaValidator** subvalidators, SizeType count) {
- // Treat allOf like oneOf and anyOf to match https://rapidjson.org/md_doc_schema.html#allOf-anyOf-oneOf
- AddErrorArray(kValidateErrorAllOf, subvalidators, count);
- //for (SizeType i = 0; i < count; ++i) {
- // MergeError(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError());
- //}
- }
- void NoneOf(ISchemaValidator** subvalidators, SizeType count) {
- AddErrorArray(kValidateErrorAnyOf, subvalidators, count);
- }
- void NotOneOf(ISchemaValidator** subvalidators, SizeType count) {
- AddErrorArray(kValidateErrorOneOf, subvalidators, count);
- }
- void MultipleOneOf(SizeType index1, SizeType index2) {
- ValueType matches(kArrayType);
- matches.PushBack(index1, GetStateAllocator());
- matches.PushBack(index2, GetStateAllocator());
- currentError_.SetObject();
- currentError_.AddMember(GetMatchesString(), matches, GetStateAllocator());
- AddCurrentError(kValidateErrorOneOfMatch);
- }
- void Disallowed() {
- currentError_.SetObject();
- AddCurrentError(kValidateErrorNot);
- }
- void DisallowedWhenWriting() {
- currentError_.SetObject();
- AddCurrentError(kValidateErrorReadOnly);
- }
- void DisallowedWhenReading() {
- currentError_.SetObject();
- AddCurrentError(kValidateErrorWriteOnly);
- }
- #define RAPIDJSON_STRING_(name, ...) \
- static const StringRefType& Get##name##String() {\
- static const Ch s[] = { __VA_ARGS__, '\0' };\
- static const StringRefType v(s, static_cast<SizeType>(sizeof(s) / sizeof(Ch) - 1)); \
- return v;\
- }
- RAPIDJSON_STRING_(InstanceRef, 'i', 'n', 's', 't', 'a', 'n', 'c', 'e', 'R', 'e', 'f')
- RAPIDJSON_STRING_(SchemaRef, 's', 'c', 'h', 'e', 'm', 'a', 'R', 'e', 'f')
- RAPIDJSON_STRING_(Expected, 'e', 'x', 'p', 'e', 'c', 't', 'e', 'd')
- RAPIDJSON_STRING_(Actual, 'a', 'c', 't', 'u', 'a', 'l')
- RAPIDJSON_STRING_(Disallowed, 'd', 'i', 's', 'a', 'l', 'l', 'o', 'w', 'e', 'd')
- RAPIDJSON_STRING_(Missing, 'm', 'i', 's', 's', 'i', 'n', 'g')
- RAPIDJSON_STRING_(Errors, 'e', 'r', 'r', 'o', 'r', 's')
- RAPIDJSON_STRING_(ErrorCode, 'e', 'r', 'r', 'o', 'r', 'C', 'o', 'd', 'e')
- RAPIDJSON_STRING_(ErrorMessage, 'e', 'r', 'r', 'o', 'r', 'M', 'e', 's', 's', 'a', 'g', 'e')
- RAPIDJSON_STRING_(Duplicates, 'd', 'u', 'p', 'l', 'i', 'c', 'a', 't', 'e', 's')
- RAPIDJSON_STRING_(Matches, 'm', 'a', 't', 'c', 'h', 'e', 's')
- #undef RAPIDJSON_STRING_
- #define RAPIDJSON_SCHEMA_HANDLE_BEGIN_(method, arg1)\
- if (!valid_) return false; \
- if ((!BeginValue() && !GetContinueOnErrors()) || (!CurrentSchema().method arg1 && !GetContinueOnErrors())) {\
- *documentStack_.template Push<Ch>() = '\0';\
- documentStack_.template Pop<Ch>(1);\
- RAPIDJSON_SCHEMA_PRINT(InvalidDocument, documentStack_.template Bottom<Ch>());\
- valid_ = false;\
- return valid_;\
- }
- #define RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2)\
- for (Context* context = schemaStack_.template Bottom<Context>(); context != schemaStack_.template End<Context>(); context++) {\
- if (context->hasher)\
- static_cast<HasherType*>(context->hasher)->method arg2;\
- if (context->validators)\
- for (SizeType i_ = 0; i_ < context->validatorCount; i_++)\
- static_cast<GenericSchemaValidator*>(context->validators[i_])->method arg2;\
- if (context->patternPropertiesValidators)\
- for (SizeType i_ = 0; i_ < context->patternPropertiesValidatorCount; i_++)\
- static_cast<GenericSchemaValidator*>(context->patternPropertiesValidators[i_])->method arg2;\
- }
- #define RAPIDJSON_SCHEMA_HANDLE_END_(method, arg2)\
- valid_ = (EndValue() || GetContinueOnErrors()) && (!outputHandler_ || outputHandler_->method arg2);\
- return valid_;
- #define RAPIDJSON_SCHEMA_HANDLE_VALUE_(method, arg1, arg2) \
- RAPIDJSON_SCHEMA_HANDLE_BEGIN_ (method, arg1);\
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(method, arg2);\
- RAPIDJSON_SCHEMA_HANDLE_END_ (method, arg2)
- bool Null() { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Null, (CurrentContext()), ( )); }
- bool Bool(bool b) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Bool, (CurrentContext(), b), (b)); }
- bool Int(int i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int, (CurrentContext(), i), (i)); }
- bool Uint(unsigned u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint, (CurrentContext(), u), (u)); }
- bool Int64(int64_t i) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Int64, (CurrentContext(), i), (i)); }
- bool Uint64(uint64_t u) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Uint64, (CurrentContext(), u), (u)); }
- bool Double(double d) { RAPIDJSON_SCHEMA_HANDLE_VALUE_(Double, (CurrentContext(), d), (d)); }
- bool RawNumber(const Ch* str, SizeType length, bool copy)
- { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
- bool String(const Ch* str, SizeType length, bool copy)
- { RAPIDJSON_SCHEMA_HANDLE_VALUE_(String, (CurrentContext(), str, length, copy), (str, length, copy)); }
- bool StartObject() {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartObject");
- RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartObject, (CurrentContext()));
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartObject, ());
- valid_ = !outputHandler_ || outputHandler_->StartObject();
- return valid_;
- }
-
- bool Key(const Ch* str, SizeType len, bool copy) {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::Key", str);
- if (!valid_) return false;
- AppendToken(str, len);
- if (!CurrentSchema().Key(CurrentContext(), str, len, copy) && !GetContinueOnErrors()) {
- valid_ = false;
- return valid_;
- }
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(Key, (str, len, copy));
- valid_ = !outputHandler_ || outputHandler_->Key(str, len, copy);
- return valid_;
- }
-
- bool EndObject(SizeType memberCount) {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndObject");
- if (!valid_) return false;
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndObject, (memberCount));
- if (!CurrentSchema().EndObject(CurrentContext(), memberCount) && !GetContinueOnErrors()) {
- valid_ = false;
- return valid_;
- }
- RAPIDJSON_SCHEMA_HANDLE_END_(EndObject, (memberCount));
- }
- bool StartArray() {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::StartArray");
- RAPIDJSON_SCHEMA_HANDLE_BEGIN_(StartArray, (CurrentContext()));
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(StartArray, ());
- valid_ = !outputHandler_ || outputHandler_->StartArray();
- return valid_;
- }
-
- bool EndArray(SizeType elementCount) {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndArray");
- if (!valid_) return false;
- RAPIDJSON_SCHEMA_HANDLE_PARALLEL_(EndArray, (elementCount));
- if (!CurrentSchema().EndArray(CurrentContext(), elementCount) && !GetContinueOnErrors()) {
- valid_ = false;
- return valid_;
- }
- RAPIDJSON_SCHEMA_HANDLE_END_(EndArray, (elementCount));
- }
- #undef RAPIDJSON_SCHEMA_HANDLE_BEGIN_
- #undef RAPIDJSON_SCHEMA_HANDLE_PARALLEL_
- #undef RAPIDJSON_SCHEMA_HANDLE_VALUE_
- // Implementation of ISchemaStateFactory<SchemaType>
- virtual ISchemaValidator* CreateSchemaValidator(const SchemaType& root, const bool inheritContinueOnErrors) {
- *documentStack_.template Push<Ch>() = '\0';
- documentStack_.template Pop<Ch>(1);
- ISchemaValidator* sv = new (GetStateAllocator().Malloc(sizeof(GenericSchemaValidator))) GenericSchemaValidator(*schemaDocument_, root, documentStack_.template Bottom<char>(), documentStack_.GetSize(),
- depth_ + 1,
- &GetStateAllocator());
- sv->SetValidateFlags(inheritContinueOnErrors ? GetValidateFlags() : GetValidateFlags() & ~static_cast<unsigned>(kValidateContinueOnErrorFlag));
- return sv;
- }
- virtual void DestroySchemaValidator(ISchemaValidator* validator) {
- GenericSchemaValidator* v = static_cast<GenericSchemaValidator*>(validator);
- v->~GenericSchemaValidator();
- StateAllocator::Free(v);
- }
- virtual void* CreateHasher() {
- return new (GetStateAllocator().Malloc(sizeof(HasherType))) HasherType(&GetStateAllocator());
- }
- virtual uint64_t GetHashCode(void* hasher) {
- return static_cast<HasherType*>(hasher)->GetHashCode();
- }
- virtual void DestroryHasher(void* hasher) {
- HasherType* h = static_cast<HasherType*>(hasher);
- h->~HasherType();
- StateAllocator::Free(h);
- }
- virtual void* MallocState(size_t size) {
- return GetStateAllocator().Malloc(size);
- }
- virtual void FreeState(void* p) {
- StateAllocator::Free(p);
- }
- // End of implementation of ISchemaStateFactory<SchemaType>
- private:
- typedef typename SchemaType::Context Context;
- typedef GenericValue<UTF8<>, StateAllocator> HashCodeArray;
- typedef internal::Hasher<EncodingType, StateAllocator> HasherType;
- GenericSchemaValidator(
- const SchemaDocumentType& schemaDocument,
- const SchemaType& root,
- const char* basePath, size_t basePathSize,
- unsigned depth,
- StateAllocator* allocator = 0,
- size_t schemaStackCapacity = kDefaultSchemaStackCapacity,
- size_t documentStackCapacity = kDefaultDocumentStackCapacity)
- :
- schemaDocument_(&schemaDocument),
- root_(root),
- stateAllocator_(allocator),
- ownStateAllocator_(0),
- schemaStack_(allocator, schemaStackCapacity),
- documentStack_(allocator, documentStackCapacity),
- outputHandler_(0),
- error_(kObjectType),
- currentError_(),
- missingDependents_(),
- valid_(true),
- flags_(kValidateDefaultFlags),
- depth_(depth)
- {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::GenericSchemaValidator (internal)", basePath && basePathSize ? basePath : "");
- if (basePath && basePathSize)
- memcpy(documentStack_.template Push<char>(basePathSize), basePath, basePathSize);
- }
- StateAllocator& GetStateAllocator() {
- if (!stateAllocator_)
- stateAllocator_ = ownStateAllocator_ = RAPIDJSON_NEW(StateAllocator)();
- return *stateAllocator_;
- }
- bool GetContinueOnErrors() const {
- return flags_ & kValidateContinueOnErrorFlag;
- }
- bool BeginValue() {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::BeginValue");
- if (schemaStack_.Empty())
- PushSchema(root_);
- else {
- if (CurrentContext().inArray)
- internal::TokenHelper<internal::Stack<StateAllocator>, Ch>::AppendIndexToken(documentStack_, CurrentContext().arrayElementIndex);
- if (!CurrentSchema().BeginValue(CurrentContext()) && !GetContinueOnErrors())
- return false;
- SizeType count = CurrentContext().patternPropertiesSchemaCount;
- const SchemaType** sa = CurrentContext().patternPropertiesSchemas;
- typename Context::PatternValidatorType patternValidatorType = CurrentContext().valuePatternValidatorType;
- bool valueUniqueness = CurrentContext().valueUniqueness;
- RAPIDJSON_ASSERT(CurrentContext().valueSchema);
- PushSchema(*CurrentContext().valueSchema);
- if (count > 0) {
- CurrentContext().objectPatternValidatorType = patternValidatorType;
- ISchemaValidator**& va = CurrentContext().patternPropertiesValidators;
- SizeType& validatorCount = CurrentContext().patternPropertiesValidatorCount;
- va = static_cast<ISchemaValidator**>(MallocState(sizeof(ISchemaValidator*) * count));
- std::memset(va, 0, sizeof(ISchemaValidator*) * count);
- for (SizeType i = 0; i < count; i++)
- va[validatorCount++] = CreateSchemaValidator(*sa[i], true); // Inherit continueOnError
- }
- CurrentContext().arrayUniqueness = valueUniqueness;
- }
- return true;
- }
- bool EndValue() {
- RAPIDJSON_SCHEMA_PRINT(Method, "GenericSchemaValidator::EndValue");
- if (!CurrentSchema().EndValue(CurrentContext()) && !GetContinueOnErrors())
- return false;
- GenericStringBuffer<EncodingType> sb;
- schemaDocument_->GetPointer(&CurrentSchema()).StringifyUriFragment(sb);
- *documentStack_.template Push<Ch>() = '\0';
- documentStack_.template Pop<Ch>(1);
- RAPIDJSON_SCHEMA_PRINT(ValidatorPointers, sb.GetString(), documentStack_.template Bottom<Ch>(), depth_);
- void* hasher = CurrentContext().hasher;
- uint64_t h = hasher && CurrentContext().arrayUniqueness ? static_cast<HasherType*>(hasher)->GetHashCode() : 0;
-
- PopSchema();
- if (!schemaStack_.Empty()) {
- Context& context = CurrentContext();
- // Only check uniqueness if there is a hasher
- if (hasher && context.valueUniqueness) {
- HashCodeArray* a = static_cast<HashCodeArray*>(context.arrayElementHashCodes);
- if (!a)
- CurrentContext().arrayElementHashCodes = a = new (GetStateAllocator().Malloc(sizeof(HashCodeArray))) HashCodeArray(kArrayType);
- for (typename HashCodeArray::ConstValueIterator itr = a->Begin(); itr != a->End(); ++itr)
- if (itr->GetUint64() == h) {
- DuplicateItems(static_cast<SizeType>(itr - a->Begin()), a->Size());
- // Cleanup before returning if continuing
- if (GetContinueOnErrors()) {
- a->PushBack(h, GetStateAllocator());
- while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/');
- }
- RAPIDJSON_INVALID_KEYWORD_RETURN(kValidateErrorUniqueItems);
- }
- a->PushBack(h, GetStateAllocator());
- }
- }
- // Remove the last token of document pointer
- while (!documentStack_.Empty() && *documentStack_.template Pop<Ch>(1) != '/')
- ;
- return true;
- }
- void AppendToken(const Ch* str, SizeType len) {
- documentStack_.template Reserve<Ch>(1 + len * 2); // worst case all characters are escaped as two characters
- *documentStack_.template PushUnsafe<Ch>() = '/';
- for (SizeType i = 0; i < len; i++) {
- if (str[i] == '~') {
- *documentStack_.template PushUnsafe<Ch>() = '~';
- *documentStack_.template PushUnsafe<Ch>() = '0';
- }
- else if (str[i] == '/') {
- *documentStack_.template PushUnsafe<Ch>() = '~';
- *documentStack_.template PushUnsafe<Ch>() = '1';
- }
- else
- *documentStack_.template PushUnsafe<Ch>() = str[i];
- }
- }
- RAPIDJSON_FORCEINLINE void PushSchema(const SchemaType& schema) { new (schemaStack_.template Push<Context>()) Context(*this, *this, &schema, flags_); }
-
- RAPIDJSON_FORCEINLINE void PopSchema() {
- Context* c = schemaStack_.template Pop<Context>(1);
- if (HashCodeArray* a = static_cast<HashCodeArray*>(c->arrayElementHashCodes)) {
- a->~HashCodeArray();
- StateAllocator::Free(a);
- }
- c->~Context();
- }
- void AddErrorInstanceLocation(ValueType& result, bool parent) {
- GenericStringBuffer<EncodingType> sb;
- PointerType instancePointer = GetInvalidDocumentPointer();
- ((parent && instancePointer.GetTokenCount() > 0)
- ? PointerType(instancePointer.GetTokens(), instancePointer.GetTokenCount() - 1)
- : instancePointer).StringifyUriFragment(sb);
- ValueType instanceRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
- GetStateAllocator());
- result.AddMember(GetInstanceRefString(), instanceRef, GetStateAllocator());
- }
- void AddErrorSchemaLocation(ValueType& result, PointerType schema = PointerType()) {
- GenericStringBuffer<EncodingType> sb;
- SizeType len = CurrentSchema().GetURI().GetStringLength();
- if (len) memcpy(sb.Push(len), CurrentSchema().GetURI().GetString(), len * sizeof(Ch));
- if (schema.GetTokenCount()) schema.StringifyUriFragment(sb);
- else GetInvalidSchemaPointer().StringifyUriFragment(sb);
- ValueType schemaRef(sb.GetString(), static_cast<SizeType>(sb.GetSize() / sizeof(Ch)),
- GetStateAllocator());
- result.AddMember(GetSchemaRefString(), schemaRef, GetStateAllocator());
- }
- void AddErrorCode(ValueType& result, const ValidateErrorCode code) {
- result.AddMember(GetErrorCodeString(), code, GetStateAllocator());
- }
- void AddError(ValueType& keyword, ValueType& error) {
- typename ValueType::MemberIterator member = error_.FindMember(keyword);
- if (member == error_.MemberEnd())
- error_.AddMember(keyword, error, GetStateAllocator());
- else {
- if (member->value.IsObject()) {
- ValueType errors(kArrayType);
- errors.PushBack(member->value, GetStateAllocator());
- member->value = errors;
- }
- member->value.PushBack(error, GetStateAllocator());
- }
- }
- void AddCurrentError(const ValidateErrorCode code, bool parent = false) {
- AddErrorCode(currentError_, code);
- AddErrorInstanceLocation(currentError_, parent);
- AddErrorSchemaLocation(currentError_);
- AddError(ValueType(SchemaType::GetValidateErrorKeyword(code), GetStateAllocator(), false).Move(), currentError_);
- }
- void MergeError(ValueType& other) {
- for (typename ValueType::MemberIterator it = other.MemberBegin(), end = other.MemberEnd(); it != end; ++it) {
- AddError(it->name, it->value);
- }
- }
- void AddNumberError(const ValidateErrorCode code, ValueType& actual, const SValue& expected,
- const typename SchemaType::ValueType& (*exclusive)() = 0) {
- currentError_.SetObject();
- currentError_.AddMember(GetActualString(), actual, GetStateAllocator());
- currentError_.AddMember(GetExpectedString(), ValueType(expected, GetStateAllocator()).Move(), GetStateAllocator());
- if (exclusive)
- currentError_.AddMember(ValueType(exclusive(), GetStateAllocator()).Move(), true, GetStateAllocator());
- AddCurrentError(code);
- }
- void AddErrorArray(const ValidateErrorCode code,
- ISchemaValidator** subvalidators, SizeType count) {
- ValueType errors(kArrayType);
- for (SizeType i = 0; i < count; ++i)
- errors.PushBack(static_cast<GenericSchemaValidator*>(subvalidators[i])->GetError(), GetStateAllocator());
- currentError_.SetObject();
- currentError_.AddMember(GetErrorsString(), errors, GetStateAllocator());
- AddCurrentError(code);
- }
- const SchemaType& CurrentSchema() const { return *schemaStack_.template Top<Context>()->schema; }
- Context& CurrentContext() { return *schemaStack_.template Top<Context>(); }
- const Context& CurrentContext() const { return *schemaStack_.template Top<Context>(); }
- static const size_t kDefaultSchemaStackCapacity = 1024;
- static const size_t kDefaultDocumentStackCapacity = 256;
- const SchemaDocumentType* schemaDocument_;
- const SchemaType& root_;
- StateAllocator* stateAllocator_;
- StateAllocator* ownStateAllocator_;
- internal::Stack<StateAllocator> schemaStack_; //!< stack to store the current path of schema (BaseSchemaType *)
- internal::Stack<StateAllocator> documentStack_; //!< stack to store the current path of validating document (Ch)
- OutputHandler* outputHandler_;
- ValueType error_;
- ValueType currentError_;
- ValueType missingDependents_;
- bool valid_;
- unsigned flags_;
- unsigned depth_;
- };
- typedef GenericSchemaValidator<SchemaDocument> SchemaValidator;
- ///////////////////////////////////////////////////////////////////////////////
- // SchemaValidatingReader
- //! A helper class for parsing with validation.
- /*!
- This helper class is a functor, designed as a parameter of \ref GenericDocument::Populate().
- \tparam parseFlags Combination of \ref ParseFlag.
- \tparam InputStream Type of input stream, implementing Stream concept.
- \tparam SourceEncoding Encoding of the input stream.
- \tparam SchemaDocumentType Type of schema document.
- \tparam StackAllocator Allocator type for stack.
- */
- template <
- unsigned parseFlags,
- typename InputStream,
- typename SourceEncoding,
- typename SchemaDocumentType = SchemaDocument,
- typename StackAllocator = CrtAllocator>
- class SchemaValidatingReader {
- public:
- typedef typename SchemaDocumentType::PointerType PointerType;
- typedef typename InputStream::Ch Ch;
- typedef GenericValue<SourceEncoding, StackAllocator> ValueType;
- //! Constructor
- /*!
- \param is Input stream.
- \param sd Schema document.
- */
- SchemaValidatingReader(InputStream& is, const SchemaDocumentType& sd) : is_(is), sd_(sd), invalidSchemaKeyword_(), invalidSchemaCode_(kValidateErrorNone), error_(kObjectType), isValid_(true) {}
- template <typename Handler>
- bool operator()(Handler& handler) {
- GenericReader<SourceEncoding, typename SchemaDocumentType::EncodingType, StackAllocator> reader;
- GenericSchemaValidator<SchemaDocumentType, Handler> validator(sd_, handler);
- parseResult_ = reader.template Parse<parseFlags>(is_, validator);
- isValid_ = validator.IsValid();
- if (isValid_) {
- invalidSchemaPointer_ = PointerType();
- invalidSchemaKeyword_ = 0;
- invalidDocumentPointer_ = PointerType();
- error_.SetObject();
- }
- else {
- invalidSchemaPointer_ = validator.GetInvalidSchemaPointer();
- invalidSchemaKeyword_ = validator.GetInvalidSchemaKeyword();
- invalidSchemaCode_ = validator.GetInvalidSchemaCode();
- invalidDocumentPointer_ = validator.GetInvalidDocumentPointer();
- error_.CopyFrom(validator.GetError(), allocator_);
- }
- return parseResult_;
- }
- const ParseResult& GetParseResult() const { return parseResult_; }
- bool IsValid() const { return isValid_; }
- const PointerType& GetInvalidSchemaPointer() const { return invalidSchemaPointer_; }
- const Ch* GetInvalidSchemaKeyword() const { return invalidSchemaKeyword_; }
- const PointerType& GetInvalidDocumentPointer() const { return invalidDocumentPointer_; }
- const ValueType& GetError() const { return error_; }
- ValidateErrorCode GetInvalidSchemaCode() const { return invalidSchemaCode_; }
- private:
- InputStream& is_;
- const SchemaDocumentType& sd_;
- ParseResult parseResult_;
- PointerType invalidSchemaPointer_;
- const Ch* invalidSchemaKeyword_;
- PointerType invalidDocumentPointer_;
- ValidateErrorCode invalidSchemaCode_;
- StackAllocator allocator_;
- ValueType error_;
- bool isValid_;
- };
- RAPIDJSON_NAMESPACE_END
- RAPIDJSON_DIAG_POP
- #endif // RAPIDJSON_SCHEMA_H_
|