handle internal refs properly
This commit is contained in:
parent
fe1a29ca69
commit
6b57738e4a
BIN
bin/unittestschema/idandref.json
Normal file
BIN
bin/unittestschema/idandref.json
Normal file
Binary file not shown.
@ -16,6 +16,7 @@
|
|||||||
#define RAPIDJSON_POINTER_H_
|
#define RAPIDJSON_POINTER_H_
|
||||||
|
|
||||||
#include "document.h"
|
#include "document.h"
|
||||||
|
#include "uri.h"
|
||||||
#include "internal/itoa.h"
|
#include "internal/itoa.h"
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
@ -80,6 +81,8 @@ class GenericPointer {
|
|||||||
public:
|
public:
|
||||||
typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value
|
typedef typename ValueType::EncodingType EncodingType; //!< Encoding type from Value
|
||||||
typedef typename ValueType::Ch Ch; //!< Character type from Value
|
typedef typename ValueType::Ch Ch; //!< Character type from Value
|
||||||
|
typedef GenericUri<ValueType, Allocator> UriType;
|
||||||
|
|
||||||
|
|
||||||
//! A token is the basic units of internal representation.
|
//! A token is the basic units of internal representation.
|
||||||
/*!
|
/*!
|
||||||
@ -520,6 +523,69 @@ public:
|
|||||||
|
|
||||||
//@}
|
//@}
|
||||||
|
|
||||||
|
//!@name Compute URI
|
||||||
|
//@{
|
||||||
|
|
||||||
|
//! Compute the in-scope URI for a subtree.
|
||||||
|
// For use with JSON pointers into JSON schema documents.
|
||||||
|
/*!
|
||||||
|
\param root Root value of a DOM sub-tree to be resolved. It can be any value other than document root.
|
||||||
|
\param unresolvedTokenIndex If the pointer cannot resolve a token in the pointer, this parameter can obtain the index of unresolved token.
|
||||||
|
\return Uri if it can be resolved. Otherwise null.
|
||||||
|
|
||||||
|
\note
|
||||||
|
There are only 3 situations when a URI cannot be resolved:
|
||||||
|
1. A value in the path is not an array nor object.
|
||||||
|
2. An object value does not contain the token.
|
||||||
|
3. A token is out of range of an array value.
|
||||||
|
|
||||||
|
Use unresolvedTokenIndex to retrieve the token index.
|
||||||
|
*/
|
||||||
|
UriType GetUri(ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const {
|
||||||
|
static const Ch kIdString[] = { 'i', 'd', '\0' };
|
||||||
|
static const ValueType kIdValue(kIdString, 2);
|
||||||
|
UriType base = rootUri;
|
||||||
|
RAPIDJSON_ASSERT(IsValid());
|
||||||
|
ValueType* v = &root;
|
||||||
|
for (const Token *t = tokens_; t != tokens_ + tokenCount_; ++t) {
|
||||||
|
switch (v->GetType()) {
|
||||||
|
case kObjectType:
|
||||||
|
{
|
||||||
|
// See if we have an id, and if so resolve with the current base
|
||||||
|
typename ValueType::MemberIterator m = v->FindMember(kIdValue);
|
||||||
|
if (m != v->MemberEnd() && (m->value).IsString()) {
|
||||||
|
UriType here = UriType(m->value);
|
||||||
|
here.Resolve(base);
|
||||||
|
base = here;
|
||||||
|
}
|
||||||
|
m = v->FindMember(GenericValue<EncodingType>(GenericStringRef<Ch>(t->name, t->length)));
|
||||||
|
if (m == v->MemberEnd())
|
||||||
|
break;
|
||||||
|
v = &m->value;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
case kArrayType:
|
||||||
|
if (t->index == kPointerInvalidIndex || t->index >= v->Size())
|
||||||
|
break;
|
||||||
|
v = &((*v)[t->index]);
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error: unresolved token
|
||||||
|
if (unresolvedTokenIndex)
|
||||||
|
*unresolvedTokenIndex = static_cast<size_t>(t - tokens_);
|
||||||
|
return UriType();
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
UriType GetUri(const ValueType& root, const UriType& rootUri, size_t* unresolvedTokenIndex = 0) const {
|
||||||
|
return GetUri(const_cast<ValueType&>(root), rootUri, unresolvedTokenIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//!@name Query value
|
//!@name Query value
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// Tencent is pleased to support the open source community by making RapidJSON available->
|
// 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->
|
// Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip-> All rights reserved->
|
||||||
|
// Portions (C) Copyright IBM Corporation 2021
|
||||||
//
|
//
|
||||||
// Licensed under the MIT License (the "License"); you may not use this file except
|
// 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
|
// in compliance with the License-> You may obtain a copy of the License at
|
||||||
@ -19,6 +20,7 @@
|
|||||||
#include "pointer.h"
|
#include "pointer.h"
|
||||||
#include "stringbuffer.h"
|
#include "stringbuffer.h"
|
||||||
#include "error/en.h"
|
#include "error/en.h"
|
||||||
|
#include "uri.h"
|
||||||
#include <cmath> // abs, floor
|
#include <cmath> // abs, floor
|
||||||
|
|
||||||
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
|
#if !defined(RAPIDJSON_SCHEMA_USE_INTERNALREGEX)
|
||||||
@ -150,9 +152,6 @@ enum ValidateFlag {
|
|||||||
template <typename ValueType, typename Allocator>
|
template <typename ValueType, typename Allocator>
|
||||||
class GenericSchemaDocument;
|
class GenericSchemaDocument;
|
||||||
|
|
||||||
template <typename SchemaDocumentType>
|
|
||||||
class Uri;
|
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template <typename SchemaDocumentType>
|
template <typename SchemaDocumentType>
|
||||||
@ -435,7 +434,7 @@ public:
|
|||||||
typedef Schema<SchemaDocumentType> SchemaType;
|
typedef Schema<SchemaDocumentType> SchemaType;
|
||||||
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
||||||
typedef IValidationErrorHandler<Schema> ErrorHandler;
|
typedef IValidationErrorHandler<Schema> ErrorHandler;
|
||||||
typedef Uri<SchemaDocumentType> UriType;
|
typedef GenericUri<ValueType, AllocatorType> UriType;
|
||||||
friend class GenericSchemaDocument<ValueType, AllocatorType>;
|
friend class GenericSchemaDocument<ValueType, AllocatorType>;
|
||||||
|
|
||||||
Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
|
Schema(SchemaDocumentType* schemaDocument, const PointerType& p, const ValueType& value, const ValueType& document, AllocatorType* allocator, const UriType& id = UriType()) :
|
||||||
@ -496,7 +495,6 @@ public:
|
|||||||
// If we have an id property, resolve it with the in-scope id
|
// If we have an id property, resolve it with the in-scope id
|
||||||
if (const ValueType* v = GetMember(value, GetIdString())) {
|
if (const ValueType* v = GetMember(value, GetIdString())) {
|
||||||
if (v->IsString()) {
|
if (v->IsString()) {
|
||||||
//std::cout << "Resolving local id '" << v->.GetString() << "' with in-scope id '" << id.GetString() << "'" << std::endl;
|
|
||||||
UriType local = UriType(*v);
|
UriType local = UriType(*v);
|
||||||
local.Resolve(id_);
|
local.Resolve(id_);
|
||||||
id_ = local;
|
id_ = local;
|
||||||
@ -1592,209 +1590,6 @@ struct TokenHelper<Stack, char> {
|
|||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Uri
|
|
||||||
|
|
||||||
template <typename SchemaDocumentType>
|
|
||||||
class Uri {
|
|
||||||
public:
|
|
||||||
typedef typename SchemaDocumentType::Ch Ch;
|
|
||||||
typedef typename SchemaDocumentType::AllocatorType AllocatorType;
|
|
||||||
typedef internal::Schema<SchemaDocumentType> SchemaType;
|
|
||||||
typedef std::basic_string<Ch> String;
|
|
||||||
|
|
||||||
// Constructors
|
|
||||||
Uri() {}
|
|
||||||
|
|
||||||
Uri(const String& uri) {
|
|
||||||
Parse(uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
Uri(const Ch* uri, SizeType len) {
|
|
||||||
Parse(String(uri, len));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use with specializations of GenericValue
|
|
||||||
template<typename T> Uri(const T& uri) {
|
|
||||||
Parse(uri.template Get<String>());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getters
|
|
||||||
const String& Get() {
|
|
||||||
// Create uri_ on-demand
|
|
||||||
if (uri_.empty()) uri_ = this->GetDoc() + frag_;
|
|
||||||
return uri_; }
|
|
||||||
|
|
||||||
// Use with specializations of GenericValue
|
|
||||||
template<typename T> void Get(T& uri, AllocatorType& allocator) {
|
|
||||||
uri.template Set<String>(this->Get(), allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
const String& GetDoc() {
|
|
||||||
// Create doc_ on-demand
|
|
||||||
if (doc_.empty()) doc_ = scheme_ + auth_ + path_ + query_;
|
|
||||||
return doc_;
|
|
||||||
}
|
|
||||||
const String& GetScheme() const { return scheme_; }
|
|
||||||
const String& GetAuth() const { return auth_; }
|
|
||||||
const String& GetPath() const { return path_; }
|
|
||||||
const String& GetQuery() const { return query_; }
|
|
||||||
const String& GetFrag() const { return frag_; }
|
|
||||||
|
|
||||||
const Ch* GetString() { return this->Get().c_str(); }
|
|
||||||
SizeType GetStringLength() { return static_cast<SizeType>(this->Get().length()); }
|
|
||||||
|
|
||||||
const Ch* GetDocString() { return this->GetDoc().c_str(); }
|
|
||||||
SizeType GetDocStringLength() { return static_cast<SizeType>(this->GetDoc().length()); }
|
|
||||||
|
|
||||||
const Ch* GetFragString() const { return frag_.c_str(); }
|
|
||||||
SizeType GetFragStringLength() const { return static_cast<SizeType>(frag_.length()); }
|
|
||||||
|
|
||||||
// Resolve this URI against a base URI in accordance with URI resolution rules at
|
|
||||||
// https://tools.ietf.org/html/rfc3986
|
|
||||||
// Use for resolving an id or $ref with an in-scope id.
|
|
||||||
// This URI is updated in place where needed from the base URI.
|
|
||||||
Uri& Resolve(const Uri& base)
|
|
||||||
{
|
|
||||||
if (!scheme_.empty()) {
|
|
||||||
// Use all of this URI
|
|
||||||
RemoveDotSegments(path_);
|
|
||||||
} else {
|
|
||||||
if (!auth_.empty()) {
|
|
||||||
RemoveDotSegments(path_);
|
|
||||||
} else {
|
|
||||||
if (path_.empty()) {
|
|
||||||
path_ = base.GetPath();
|
|
||||||
if (query_.empty()) {
|
|
||||||
query_ = base.GetQuery();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
static const String slash = SchemaType::GetSlashString().GetString();
|
|
||||||
if (path_.find(slash) == 0) {
|
|
||||||
// Absolute path - replace all the path
|
|
||||||
RemoveDotSegments(path_);
|
|
||||||
} else {
|
|
||||||
// Relative path - append to path after last slash
|
|
||||||
String p;
|
|
||||||
if (!base.GetAuth().empty() && base.GetPath().empty()) p = slash;
|
|
||||||
std::size_t lastslashpos = base.GetPath().find_last_of(slash);
|
|
||||||
path_ = p + base.GetPath().substr(0, lastslashpos + 1) + path_;
|
|
||||||
RemoveDotSegments(path_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auth_ = base.GetAuth();
|
|
||||||
}
|
|
||||||
scheme_ = base.GetScheme();
|
|
||||||
}
|
|
||||||
//std::cout << " Resolved uri: " << this->GetString() << std::endl;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Parse a URI into constituent scheme, authority, path, query, fragment
|
|
||||||
// Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
|
|
||||||
// https://tools.ietf.org/html/rfc3986
|
|
||||||
void Parse(const String& uri) {
|
|
||||||
std::size_t start = 0, pos1 = 0, pos2 = 0;
|
|
||||||
const std::size_t len = uri.length();
|
|
||||||
static const String schemeEnd = SchemaType::GetSchemeEndString().GetString();
|
|
||||||
static const String authStart = SchemaType::GetAuthStartString().GetString();
|
|
||||||
static const String pathStart = SchemaType::GetSlashString().GetString();
|
|
||||||
static const String queryStart = SchemaType::GetQueryStartString().GetString();
|
|
||||||
static const String fragStart = SchemaType::GetFragStartString().GetString();
|
|
||||||
// Look for scheme ([^:/?#]+):)?
|
|
||||||
if (start < len) {
|
|
||||||
pos1 = uri.find(schemeEnd);
|
|
||||||
if (pos1 != std::string::npos) {
|
|
||||||
pos2 = uri.find_first_of(pathStart + queryStart + fragStart);
|
|
||||||
if (pos1 < pos2) {
|
|
||||||
pos1 += schemeEnd.length();
|
|
||||||
scheme_ = uri.substr(start, pos1);
|
|
||||||
start = pos1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Look for auth (//([^/?#]*))?
|
|
||||||
if (start < len) {
|
|
||||||
pos1 = uri.find(authStart, start);
|
|
||||||
if (pos1 == start) {
|
|
||||||
pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length());
|
|
||||||
auth_ = uri.substr(start, pos2 - start);
|
|
||||||
start = pos2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Look for path ([^?#]*)
|
|
||||||
if (start < len) {
|
|
||||||
pos2 = uri.find_first_of(queryStart + fragStart, start);
|
|
||||||
if (start != pos2) {
|
|
||||||
path_ = uri.substr(start, pos2 - start);
|
|
||||||
if (path_.find(pathStart) == 0) { // absolute path - normalize
|
|
||||||
RemoveDotSegments(path_);
|
|
||||||
}
|
|
||||||
start = pos2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Look for query (\?([^#]*))?
|
|
||||||
if (start < len) {
|
|
||||||
pos2 = uri.find(fragStart, start);
|
|
||||||
if (start != pos2) {
|
|
||||||
query_ = uri.substr(start, pos2 - start);
|
|
||||||
start = pos2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Look for fragment (#(.*))?
|
|
||||||
if (start < len) {
|
|
||||||
frag_ = uri.substr(start);
|
|
||||||
}
|
|
||||||
//std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove . and .. segments from a path
|
|
||||||
// https://tools.ietf.org/html/rfc3986
|
|
||||||
void RemoveDotSegments(String& path) {
|
|
||||||
String temp = path;
|
|
||||||
path.clear();
|
|
||||||
static const String slash = SchemaType::GetSlashString().GetString();
|
|
||||||
static const String dot = SchemaType::GetDotString().GetString();
|
|
||||||
std::size_t pos = 0;
|
|
||||||
// Loop through each path segment
|
|
||||||
while (pos != std::string::npos) {
|
|
||||||
//std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl;
|
|
||||||
pos = temp.find_first_of(slash);
|
|
||||||
// Get next segment
|
|
||||||
String seg = temp.substr(0, pos);
|
|
||||||
if (seg == dot) {
|
|
||||||
// Discard . segment
|
|
||||||
} else if (seg == dot + dot) {
|
|
||||||
// Backup a .. segment
|
|
||||||
// We expect to find a previously added slash at the end or nothing
|
|
||||||
std::size_t pos1 = path.find_last_of(slash);
|
|
||||||
// Make sure we don't go beyond the start
|
|
||||||
if (pos1 != std::string::npos && pos1 != 0) {
|
|
||||||
// Find the next to last slash and back up to it
|
|
||||||
pos1 = path.find_last_of(slash, pos1 - 1);
|
|
||||||
path = path.substr(0, pos1 + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Copy segment and add slash if not at end
|
|
||||||
path += seg;
|
|
||||||
if (pos != std::string::npos) path += slash;
|
|
||||||
}
|
|
||||||
// Move to next segment if not at end
|
|
||||||
if (pos != std::string::npos) temp = temp.substr(pos + 1);
|
|
||||||
}
|
|
||||||
//std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
String uri_; // Created on-demand
|
|
||||||
String doc_; // Created on-demand
|
|
||||||
String scheme_; // Includes the :
|
|
||||||
String auth_; // Includes the //
|
|
||||||
String path_; // Absolute if starts with /
|
|
||||||
String query_; // Includes the ?
|
|
||||||
String frag_; // Includes the #
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// IGenericRemoteSchemaDocumentProvider
|
// IGenericRemoteSchemaDocumentProvider
|
||||||
|
|
||||||
@ -1802,10 +1597,12 @@ template <typename SchemaDocumentType>
|
|||||||
class IGenericRemoteSchemaDocumentProvider {
|
class IGenericRemoteSchemaDocumentProvider {
|
||||||
public:
|
public:
|
||||||
typedef typename SchemaDocumentType::Ch Ch;
|
typedef typename SchemaDocumentType::Ch Ch;
|
||||||
|
typedef typename SchemaDocumentType::ValueType ValueType;
|
||||||
|
typedef typename SchemaDocumentType::AllocatorType AllocatorType;
|
||||||
|
|
||||||
virtual ~IGenericRemoteSchemaDocumentProvider() {}
|
virtual ~IGenericRemoteSchemaDocumentProvider() {}
|
||||||
virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
|
virtual const SchemaDocumentType* GetRemoteDocument(const Ch* uri, SizeType length) = 0;
|
||||||
virtual const SchemaDocumentType* GetRemoteDocument(Uri<SchemaDocumentType> uri) { return GetRemoteDocument(uri.GetDocString(), uri.GetDocStringLength()); }
|
virtual const SchemaDocumentType* GetRemoteDocument(GenericUri<ValueType, AllocatorType> uri) { return GetRemoteDocument(uri.GetBaseString(), uri.GetBaseStringLength()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1831,7 +1628,7 @@ public:
|
|||||||
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
typedef internal::Schema<GenericSchemaDocument> SchemaType;
|
||||||
typedef GenericPointer<ValueType, Allocator> PointerType;
|
typedef GenericPointer<ValueType, Allocator> PointerType;
|
||||||
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
typedef GenericValue<EncodingType, AllocatorType> SValue;
|
||||||
typedef Uri<GenericSchemaDocument> UriType;
|
typedef GenericUri<ValueType, Allocator> UriType;
|
||||||
friend class internal::Schema<GenericSchemaDocument>;
|
friend class internal::Schema<GenericSchemaDocument>;
|
||||||
template <typename, typename, typename>
|
template <typename, typename, typename>
|
||||||
friend class GenericSchemaValidator;
|
friend class GenericSchemaValidator;
|
||||||
@ -1863,20 +1660,20 @@ public:
|
|||||||
|
|
||||||
Ch noUri[1] = {0};
|
Ch noUri[1] = {0};
|
||||||
uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
|
uri_.SetString(uri ? uri : noUri, uriLength, *allocator_);
|
||||||
UriType baseId(uri_);
|
docId_ = UriType(uri_);
|
||||||
|
|
||||||
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
typeless_ = static_cast<SchemaType*>(allocator_->Malloc(sizeof(SchemaType)));
|
||||||
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, baseId);
|
new (typeless_) SchemaType(this, PointerType(), ValueType(kObjectType).Move(), ValueType(kObjectType).Move(), allocator_, docId_);
|
||||||
|
|
||||||
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
// Generate root schema, it will call CreateSchema() to create sub-schemas,
|
||||||
// And call HandleRefSchema() if there are $ref.
|
// And call HandleRefSchema() if there are $ref.
|
||||||
// PR #1393 use input pointer if supplied
|
// PR #1393 use input pointer if supplied
|
||||||
root_ = typeless_;
|
root_ = typeless_;
|
||||||
if (pointer.GetTokenCount() == 0) {
|
if (pointer.GetTokenCount() == 0) {
|
||||||
CreateSchemaRecursive(&root_, pointer, document, document, baseId);
|
CreateSchemaRecursive(&root_, pointer, document, document, docId_);
|
||||||
}
|
}
|
||||||
else if (const ValueType* v = pointer.Get(document)) {
|
else if (const ValueType* v = pointer.Get(document)) {
|
||||||
CreateSchema(&root_, pointer, *v, document, baseId);
|
CreateSchema(&root_, pointer, *v, document, docId_);
|
||||||
}
|
}
|
||||||
|
|
||||||
RAPIDJSON_ASSERT(root_ != 0);
|
RAPIDJSON_ASSERT(root_ != 0);
|
||||||
@ -1894,7 +1691,8 @@ public:
|
|||||||
typeless_(rhs.typeless_),
|
typeless_(rhs.typeless_),
|
||||||
schemaMap_(std::move(rhs.schemaMap_)),
|
schemaMap_(std::move(rhs.schemaMap_)),
|
||||||
schemaRef_(std::move(rhs.schemaRef_)),
|
schemaRef_(std::move(rhs.schemaRef_)),
|
||||||
uri_(std::move(rhs.uri_))
|
uri_(std::move(rhs.uri_)),
|
||||||
|
docId_(rhs.docId_),
|
||||||
{
|
{
|
||||||
rhs.remoteProvider_ = 0;
|
rhs.remoteProvider_ = 0;
|
||||||
rhs.allocator_ = 0;
|
rhs.allocator_ = 0;
|
||||||
@ -1945,10 +1743,10 @@ private:
|
|||||||
// Changed by PR #1393
|
// Changed by PR #1393
|
||||||
void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
void CreateSchemaRecursive(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
||||||
if (v.GetType() == kObjectType) {
|
if (v.GetType() == kObjectType) {
|
||||||
CreateSchema(schema, pointer, v, document, id);
|
UriType newid = CreateSchema(schema, pointer, v, document, id);
|
||||||
|
|
||||||
for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
|
for (typename ValueType::ConstMemberIterator itr = v.MemberBegin(); itr != v.MemberEnd(); ++itr)
|
||||||
CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, id);
|
CreateSchemaRecursive(0, pointer.Append(itr->name, allocator_), itr->value, document, newid);
|
||||||
}
|
}
|
||||||
else if (v.GetType() == kArrayType)
|
else if (v.GetType() == kArrayType)
|
||||||
for (SizeType i = 0; i < v.Size(); i++)
|
for (SizeType i = 0; i < v.Size(); i++)
|
||||||
@ -1956,20 +1754,20 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Changed by PR #1393
|
// Changed by PR #1393
|
||||||
void CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
const UriType& CreateSchema(const SchemaType** schema, const PointerType& pointer, const ValueType& v, const ValueType& document, const UriType& id) {
|
||||||
RAPIDJSON_ASSERT(pointer.IsValid());
|
RAPIDJSON_ASSERT(pointer.IsValid());
|
||||||
if (v.IsObject()) {
|
if (v.IsObject()) {
|
||||||
if (const SchemaType* sc = GetSchema(pointer)) {
|
if (const SchemaType* sc = GetSchema(pointer)) {
|
||||||
if (schema)
|
if (schema)
|
||||||
*schema = sc;
|
*schema = sc;
|
||||||
//std::cout << "Using Schema with id " << sc->GetId().GetString() << std::endl;
|
|
||||||
AddSchemaRefs(const_cast<SchemaType*>(sc));
|
AddSchemaRefs(const_cast<SchemaType*>(sc));
|
||||||
}
|
}
|
||||||
else if (!HandleRefSchema(pointer, schema, v, document, id)) {
|
else if (!HandleRefSchema(pointer, schema, v, document, id)) {
|
||||||
// The new schema adds itself and its $ref(s) to schemaMap_
|
// 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);
|
SchemaType* s = new (allocator_->Malloc(sizeof(SchemaType))) SchemaType(this, pointer, v, document, allocator_, id);
|
||||||
if (schema)
|
if (schema)
|
||||||
*schema = s;
|
*schema = s;
|
||||||
|
return s->GetId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1977,11 +1775,12 @@ private:
|
|||||||
*schema = typeless_;
|
*schema = typeless_;
|
||||||
AddSchemaRefs(typeless_);
|
AddSchemaRefs(typeless_);
|
||||||
}
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changed by PR #1393
|
// 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) {
|
bool HandleRefSchema(const PointerType& source, const SchemaType** schema, const ValueType& v, const ValueType& document, const UriType& id) {
|
||||||
//std::cout << "HandleRefSchema called with id " << id.GetString() << std::endl;
|
|
||||||
typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
|
typename ValueType::ConstMemberIterator itr = v.FindMember(SchemaType::GetRefString());
|
||||||
if (itr == v.MemberEnd())
|
if (itr == v.MemberEnd())
|
||||||
return false;
|
return false;
|
||||||
@ -1992,16 +1791,25 @@ private:
|
|||||||
if (itr->value.IsString()) {
|
if (itr->value.IsString()) {
|
||||||
SizeType len = itr->value.GetStringLength();
|
SizeType len = itr->value.GetStringLength();
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
const Ch* s = itr->value.GetString();
|
// First resolve $ref against the in-scope id
|
||||||
if (s[0] != '#') { // Remote reference - resolve $ref against the in-scope id
|
UriType scopeId = id;
|
||||||
if (remoteProvider_) {
|
|
||||||
UriType ref = UriType(itr->value);
|
UriType ref = UriType(itr->value);
|
||||||
ref.Resolve(id);
|
ref.Resolve(scopeId);
|
||||||
//std::cout << "Resolved $ref '" << s << "' against in-scope id '" << id.GetString() << "' giving '" << ref.GetDocString() << "'" << std::endl;
|
// 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_) {
|
||||||
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
|
if (const GenericSchemaDocument* remoteDocument = remoteProvider_->GetRemoteDocument(ref)) {
|
||||||
// Create a pointer from the # onwards
|
const Ch* s = ref.GetFragString();
|
||||||
const PointerType pointer(ref.GetFragString(), ref.GetFragStringLength(), allocator_);
|
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()) {
|
if (pointer.IsValid()) {
|
||||||
|
// Get the subschema
|
||||||
if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
|
if (const SchemaType *sc = remoteDocument->GetSchema(pointer)) {
|
||||||
if (schema)
|
if (schema)
|
||||||
*schema = sc;
|
*schema = sc;
|
||||||
@ -2009,32 +1817,56 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Plain name fragment, not allowed
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else { // Local reference
|
else { // Local reference
|
||||||
if (len == 1 || s[1] == '/') {
|
const Ch* s = ref.GetFragString();
|
||||||
// JSON pointer
|
len = ref.GetFragStringLength();
|
||||||
const PointerType pointer(s, len, allocator_);
|
if (len <= 1 || s[1] == '/') {
|
||||||
|
// JSON pointer fragment, relative to the resolved URI
|
||||||
|
const PointerType relPointer(s, len, allocator_);
|
||||||
|
if (relPointer.IsValid()) {
|
||||||
|
// Get the subschema
|
||||||
|
if (const ValueType *v = relPointer.Get(*base)) {
|
||||||
|
// Now get the absolute JSON pointer by adding relative to base
|
||||||
|
PointerType pointer(basePointer);
|
||||||
|
for (SizeType i = 0; i < relPointer.GetTokenCount(); i++)
|
||||||
|
pointer = pointer.Append(relPointer.GetTokens()[i], allocator_);
|
||||||
|
//GenericStringBuffer<EncodingType> sb;
|
||||||
|
//pointer.StringifyUriFragment(sb);
|
||||||
if (pointer.IsValid() && !IsCyclicRef(pointer)) {
|
if (pointer.IsValid() && !IsCyclicRef(pointer)) {
|
||||||
if (const ValueType *nv = pointer.Get(document)) {
|
// Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
||||||
CreateSchema(schema, pointer, *nv, document, id);
|
// TODO: cache pointer <-> id mapping
|
||||||
|
scopeId = pointer.GetUri(document, docId_);
|
||||||
|
CreateSchema(schema, pointer, *v, document, scopeId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Internal reference to an id
|
// Plain name fragment, relative to the resolved URI
|
||||||
const ValueType val(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.
|
||||||
PointerType pointer = PointerType();
|
PointerType pointer = PointerType();
|
||||||
ValueType *nv = FindId(document, val, pointer);
|
if (const ValueType *v = FindId(*base, ref, pointer, UriType(ref.GetBase()), true, basePointer)) {
|
||||||
if (nv && !IsCyclicRef(pointer)) {
|
if (v && !IsCyclicRef(pointer)) {
|
||||||
CreateSchema(schema, pointer, *nv, document, id);
|
//GenericStringBuffer<EncodingType> sb;
|
||||||
|
//pointer.StringifyUriFragment(sb);
|
||||||
|
// Call CreateSchema recursively, but first compute the in-scope id for the $ref target as we have jumped there
|
||||||
|
// TODO: cache pointer <-> id mapping
|
||||||
|
scopeId = pointer.GetUri(document, docId_);
|
||||||
|
CreateSchema(schema, pointer, *v, document, scopeId);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Invalid/Unknown $ref
|
// Invalid/Unknown $ref
|
||||||
if (schema)
|
if (schema)
|
||||||
@ -2043,36 +1875,46 @@ private:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//! Find the first subschema with 'id' string property matching the specified value.
|
//! Find the first subschema with a resolved 'id' that matches the specified URI.
|
||||||
// Return a pointer to the subschema and its JSON pointer.
|
// If full specified use all URI else ignore fragment.
|
||||||
ValueType* FindId(const ValueType& doc, const ValueType& findval, PointerType& resptr, const PointerType& here = PointerType()) const {
|
// 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;
|
SizeType i = 0;
|
||||||
ValueType* resval = 0;
|
ValueType* resval = 0;
|
||||||
switch(doc.GetType()) {
|
UriType tempuri = finduri;
|
||||||
case kObjectType:
|
UriType localuri = baseuri;
|
||||||
for (typename ValueType::ConstMemberIterator m = doc.MemberBegin(); m != doc.MemberEnd(); ++m) {
|
if (doc.GetType() == kObjectType) {
|
||||||
if (m->name == SchemaType::GetIdString() && m->value.GetType() == kStringType && m->value == findval) {
|
// Establish the base URI of this object
|
||||||
// Found the 'id' with the value
|
typename ValueType::ConstMemberIterator m = doc.FindMember(SchemaType::GetIdString());
|
||||||
|
if (m != doc.MemberEnd() && m->value.GetType() == kStringType) {
|
||||||
|
localuri = UriType(m->value);
|
||||||
|
localuri.Resolve(baseuri);
|
||||||
|
}
|
||||||
|
// See if it matches
|
||||||
|
if (localuri.Match(finduri, full)) {
|
||||||
resval = const_cast<ValueType *>(&doc);
|
resval = const_cast<ValueType *>(&doc);
|
||||||
resptr = here;
|
resptr = here;
|
||||||
} else if (m->value.GetType() == kObjectType || m->value.GetType() == kArrayType) {
|
return resval;
|
||||||
resval = FindId(m->value, findval, resptr, here.Append(m->name.GetString(), m->name.GetStringLength(), allocator_));
|
}
|
||||||
|
// No match, continue looking
|
||||||
|
for (typename ValueType::ConstMemberIterator 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;
|
if (resval) break;
|
||||||
}
|
}
|
||||||
return resval;
|
} else if (doc.GetType() == kArrayType) {
|
||||||
case kArrayType:
|
// Continue looking
|
||||||
for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
|
for (typename ValueType::ConstValueIterator v = doc.Begin(); v != doc.End(); ++v) {
|
||||||
if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
|
if (v->GetType() == kObjectType || v->GetType() == kArrayType) {
|
||||||
resval = FindId(*v, findval, resptr, here.Append(i, allocator_));
|
resval = FindId(*v, finduri, resptr, localuri, full, here.Append(i, allocator_));
|
||||||
}
|
}
|
||||||
if (resval) break;
|
if (resval) break;
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
return resval;
|
|
||||||
default:
|
|
||||||
return resval;
|
|
||||||
}
|
}
|
||||||
|
return resval;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added by PR #1393
|
// Added by PR #1393
|
||||||
@ -2119,6 +1961,7 @@ private:
|
|||||||
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
internal::Stack<Allocator> schemaMap_; // Stores created Pointer -> Schemas
|
||||||
internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
|
internal::Stack<Allocator> schemaRef_; // Stores Pointer(s) from $ref(s) until resolved
|
||||||
SValue uri_; // Schema document URI
|
SValue uri_; // Schema document URI
|
||||||
|
UriType docId_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! GenericSchemaDocument using Value type.
|
//! GenericSchemaDocument using Value type.
|
||||||
|
259
include/rapidjson/uri.h
Normal file
259
include/rapidjson/uri.h
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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_URI_H_
|
||||||
|
#define RAPIDJSON_URI_H_
|
||||||
|
|
||||||
|
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
|
||||||
|
#include <utility> // std::move
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
RAPIDJSON_DIAG_PUSH
|
||||||
|
RAPIDJSON_DIAG_OFF(c++98-compat)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
RAPIDJSON_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GenericUri
|
||||||
|
|
||||||
|
template <typename ValueType, typename Allocator=CrtAllocator>
|
||||||
|
class GenericUri {
|
||||||
|
public:
|
||||||
|
typedef typename ValueType::Ch Ch;
|
||||||
|
typedef std::basic_string<Ch> String;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
GenericUri() {}
|
||||||
|
|
||||||
|
GenericUri(const String& uri) {
|
||||||
|
Parse(uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
GenericUri(const Ch* uri, SizeType len) {
|
||||||
|
Parse(String(uri, len));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use with specializations of GenericValue
|
||||||
|
template<typename T> GenericUri(const T& uri) {
|
||||||
|
Parse(uri.template Get<String>());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
const String& Get() const { return uri_; }
|
||||||
|
|
||||||
|
// Use with specializations of GenericValue
|
||||||
|
template<typename T> void Get(T& uri, Allocator& allocator) {
|
||||||
|
uri.template Set<String>(this->Get(), allocator);
|
||||||
|
}
|
||||||
|
|
||||||
|
const String& GetBase() const { return base_; }
|
||||||
|
const String& GetScheme() const { return scheme_; }
|
||||||
|
const String& GetAuth() const { return auth_; }
|
||||||
|
const String& GetPath() const { return path_; }
|
||||||
|
const String& GetQuery() const { return query_; }
|
||||||
|
const String& GetFrag() const { return frag_; }
|
||||||
|
|
||||||
|
const Ch* GetString() const { return uri_.c_str(); }
|
||||||
|
SizeType GetStringLength() const { return static_cast<SizeType>(uri_.length()); }
|
||||||
|
|
||||||
|
const Ch* GetBaseString() const { return base_.c_str(); }
|
||||||
|
SizeType GetBaseStringLength() const { return static_cast<SizeType>(base_.length()); }
|
||||||
|
|
||||||
|
const Ch* GetFragString() const { return frag_.c_str(); }
|
||||||
|
SizeType GetFragStringLength() const { return static_cast<SizeType>(frag_.length()); }
|
||||||
|
|
||||||
|
// Resolve this URI against another URI in accordance with URI resolution rules at
|
||||||
|
// https://tools.ietf.org/html/rfc3986
|
||||||
|
// Use for resolving an id or $ref with an in-scope id.
|
||||||
|
// This URI is updated in place where needed from the base URI.
|
||||||
|
GenericUri& Resolve(const GenericUri& uri) {
|
||||||
|
if (!scheme_.empty()) {
|
||||||
|
// Use all of this URI
|
||||||
|
RemoveDotSegments(path_);
|
||||||
|
} else {
|
||||||
|
if (!auth_.empty()) {
|
||||||
|
RemoveDotSegments(path_);
|
||||||
|
} else {
|
||||||
|
if (path_.empty()) {
|
||||||
|
path_ = uri.GetPath();
|
||||||
|
if (query_.empty()) {
|
||||||
|
query_ = uri.GetQuery();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
static const String slash = GetSlashString().GetString();
|
||||||
|
if (path_.find(slash) == 0) {
|
||||||
|
// Absolute path - replace all the path
|
||||||
|
RemoveDotSegments(path_);
|
||||||
|
} else {
|
||||||
|
// Relative path - append to path after last slash
|
||||||
|
String p;
|
||||||
|
if (!uri.GetAuth().empty() && uri.GetPath().empty()) p = slash;
|
||||||
|
std::size_t lastslashpos = uri.GetPath().find_last_of(slash);
|
||||||
|
path_ = p + uri.GetPath().substr(0, lastslashpos + 1) + path_;
|
||||||
|
RemoveDotSegments(path_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auth_ = uri.GetAuth();
|
||||||
|
}
|
||||||
|
scheme_ = uri.GetScheme();
|
||||||
|
}
|
||||||
|
base_ = scheme_ + auth_ + path_ + query_;
|
||||||
|
uri_ = base_ + frag_;
|
||||||
|
//std::cout << " Resolved uri: " << uri_ << std::endl;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Match(const GenericUri& uri, bool full) const {
|
||||||
|
if (full)
|
||||||
|
return uri_ == uri.Get();
|
||||||
|
else
|
||||||
|
return base_ == uri.GetBase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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_(SchemeEnd, ':')
|
||||||
|
RAPIDJSON_STRING_(AuthStart, '/', '/')
|
||||||
|
RAPIDJSON_STRING_(QueryStart, '?')
|
||||||
|
RAPIDJSON_STRING_(FragStart, '#')
|
||||||
|
RAPIDJSON_STRING_(Slash, '/')
|
||||||
|
RAPIDJSON_STRING_(Dot, '.')
|
||||||
|
|
||||||
|
#undef RAPIDJSON_STRING_
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Parse a URI into constituent scheme, authority, path, query, fragment
|
||||||
|
// Supports URIs that match regex ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? as per
|
||||||
|
// https://tools.ietf.org/html/rfc3986
|
||||||
|
void Parse(const String& uri) {
|
||||||
|
std::size_t start = 0, pos1 = 0, pos2 = 0;
|
||||||
|
const std::size_t len = uri.length();
|
||||||
|
static const String schemeEnd = GetSchemeEndString().GetString();
|
||||||
|
static const String authStart = GetAuthStartString().GetString();
|
||||||
|
static const String pathStart = GetSlashString().GetString();
|
||||||
|
static const String queryStart = GetQueryStartString().GetString();
|
||||||
|
static const String fragStart = GetFragStartString().GetString();
|
||||||
|
// Look for scheme ([^:/?#]+):)?
|
||||||
|
if (start < len) {
|
||||||
|
pos1 = uri.find(schemeEnd);
|
||||||
|
if (pos1 != std::string::npos) {
|
||||||
|
pos2 = uri.find_first_of(pathStart + queryStart + fragStart);
|
||||||
|
if (pos1 < pos2) {
|
||||||
|
pos1 += schemeEnd.length();
|
||||||
|
scheme_ = uri.substr(start, pos1);
|
||||||
|
start = pos1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for auth (//([^/?#]*))?
|
||||||
|
if (start < len) {
|
||||||
|
pos1 = uri.find(authStart, start);
|
||||||
|
if (pos1 == start) {
|
||||||
|
pos2 = uri.find_first_of(pathStart + queryStart + fragStart, start + authStart.length());
|
||||||
|
auth_ = uri.substr(start, pos2 - start);
|
||||||
|
start = pos2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for path ([^?#]*)
|
||||||
|
if (start < len) {
|
||||||
|
pos2 = uri.find_first_of(queryStart + fragStart, start);
|
||||||
|
if (start != pos2) {
|
||||||
|
path_ = uri.substr(start, pos2 - start);
|
||||||
|
if (path_.find(pathStart) == 0) { // absolute path - normalize
|
||||||
|
RemoveDotSegments(path_);
|
||||||
|
}
|
||||||
|
start = pos2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for query (\?([^#]*))?
|
||||||
|
if (start < len) {
|
||||||
|
pos2 = uri.find(fragStart, start);
|
||||||
|
if (start != pos2) {
|
||||||
|
query_ = uri.substr(start, pos2 - start);
|
||||||
|
start = pos2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Look for fragment (#(.*))?
|
||||||
|
if (start < len) {
|
||||||
|
frag_ = uri.substr(start);
|
||||||
|
}
|
||||||
|
base_ = scheme_ + auth_ + path_ + query_;
|
||||||
|
uri_ = base_ + frag_;
|
||||||
|
//std::cout << " Parsed uri: " << "s: " << scheme_.c_str() << " a: " << auth_.c_str() << " p: " << path_.c_str() << " q: " << query_.c_str() << " f: " << frag_.c_str() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove . and .. segments from a path
|
||||||
|
// https://tools.ietf.org/html/rfc3986
|
||||||
|
void RemoveDotSegments(String& path) {
|
||||||
|
String temp = path;
|
||||||
|
path.clear();
|
||||||
|
static const String slash = GetSlashString().GetString();
|
||||||
|
static const String dot = GetDotString().GetString();
|
||||||
|
std::size_t pos = 0;
|
||||||
|
// Loop through each path segment
|
||||||
|
while (pos != std::string::npos) {
|
||||||
|
//std::cout << "Temp: '" << temp.c_str() << "' Path: '" << path.c_str() << "'" << std::endl;
|
||||||
|
pos = temp.find_first_of(slash);
|
||||||
|
// Get next segment
|
||||||
|
String seg = temp.substr(0, pos);
|
||||||
|
if (seg == dot) {
|
||||||
|
// Discard . segment
|
||||||
|
} else if (seg == dot + dot) {
|
||||||
|
// Backup a .. segment
|
||||||
|
// We expect to find a previously added slash at the end or nothing
|
||||||
|
std::size_t pos1 = path.find_last_of(slash);
|
||||||
|
// Make sure we don't go beyond the start
|
||||||
|
if (pos1 != std::string::npos && pos1 != 0) {
|
||||||
|
// Find the next to last slash and back up to it
|
||||||
|
pos1 = path.find_last_of(slash, pos1 - 1);
|
||||||
|
path = path.substr(0, pos1 + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Copy segment and add slash if not at end
|
||||||
|
path += seg;
|
||||||
|
if (pos != std::string::npos) path += slash;
|
||||||
|
}
|
||||||
|
// Move to next segment if not at end
|
||||||
|
if (pos != std::string::npos) temp = temp.substr(pos + 1);
|
||||||
|
}
|
||||||
|
//std::cout << "Final Temp: '" << temp.c_str() << "' Final Path: '" << path.c_str() << "'" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
String uri_ = String(); // Full uri
|
||||||
|
String base_ = String(); // Everything except fragment
|
||||||
|
String scheme_ = String(); // Includes the :
|
||||||
|
String auth_ = String(); // Includes the //
|
||||||
|
String path_ = String(); // Absolute if starts with /
|
||||||
|
String query_ = String(); // Includes the ?
|
||||||
|
String frag_ = String(); // Includes the #
|
||||||
|
};
|
||||||
|
|
||||||
|
//! GenericUri for Value (UTF-8, default allocator).
|
||||||
|
typedef GenericUri<Value> Uri;
|
||||||
|
|
||||||
|
RAPIDJSON_NAMESPACE_END
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // RAPIDJSON_URI_H_
|
@ -26,6 +26,7 @@ set(UNITTEST_SOURCES
|
|||||||
stringbuffertest.cpp
|
stringbuffertest.cpp
|
||||||
strtodtest.cpp
|
strtodtest.cpp
|
||||||
unittest.cpp
|
unittest.cpp
|
||||||
|
uritest.cpp
|
||||||
valuetest.cpp
|
valuetest.cpp
|
||||||
writertest.cpp)
|
writertest.cpp)
|
||||||
|
|
||||||
|
@ -648,6 +648,48 @@ TEST(Pointer, Create) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char kJsonIds[] = "{\n"
|
||||||
|
" \"id\": \"/root/\","
|
||||||
|
" \"foo\":[\"bar\", \"baz\", {\"id\": \"inarray\", \"child\": 1}],\n"
|
||||||
|
" \"int\" : 2,\n"
|
||||||
|
" \"str\" : \"val\",\n"
|
||||||
|
" \"obj\": {\"id\": \"inobj\", \"child\": 3},\n"
|
||||||
|
" \"jbo\": {\"id\": true, \"child\": 4}\n"
|
||||||
|
"}";
|
||||||
|
|
||||||
|
|
||||||
|
TEST(Pointer, GetUri) {
|
||||||
|
typedef std::basic_string<Value::Ch> String;
|
||||||
|
Document d;
|
||||||
|
d.Parse(kJsonIds);
|
||||||
|
|
||||||
|
String doc = String("http://doc");
|
||||||
|
EXPECT_TRUE((Pointer("").GetUri(d, Pointer::UriType(doc)).Get()) == doc);
|
||||||
|
EXPECT_TRUE((Pointer("/foo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/foo/0").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/foo/2").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/foo/2/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inarray");
|
||||||
|
EXPECT_TRUE((Pointer("/int").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/str").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/obj").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/obj/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/inobj");
|
||||||
|
EXPECT_TRUE((Pointer("/jbo").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
EXPECT_TRUE((Pointer("/jbo/child").GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/"); // id not string
|
||||||
|
|
||||||
|
size_t unresolvedTokenIndex;
|
||||||
|
EXPECT_TRUE((Pointer("/foo/3").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // Out of boundary
|
||||||
|
EXPECT_EQ(1u, unresolvedTokenIndex);
|
||||||
|
EXPECT_TRUE((Pointer("/foo/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo" is an array, cannot query by "a"
|
||||||
|
EXPECT_EQ(1u, unresolvedTokenIndex);
|
||||||
|
EXPECT_TRUE((Pointer("/foo/0/0").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query
|
||||||
|
EXPECT_EQ(2u, unresolvedTokenIndex);
|
||||||
|
EXPECT_TRUE((Pointer("/foo/0/a").GetUri(d, Pointer::UriType(doc), &unresolvedTokenIndex).Get()) == ""); // "/foo/0" is an string, cannot further query
|
||||||
|
EXPECT_EQ(2u, unresolvedTokenIndex);
|
||||||
|
|
||||||
|
Pointer::Token tokens[] = { { "foo ...", 3, kPointerInvalidIndex } };
|
||||||
|
EXPECT_TRUE((Pointer(tokens, 1).GetUri(d, Pointer::UriType(doc)).Get()) == "http://doc/root/");
|
||||||
|
}
|
||||||
|
|
||||||
TEST(Pointer, Get) {
|
TEST(Pointer, Get) {
|
||||||
Document d;
|
Document d;
|
||||||
d.Parse(kJson);
|
d.Parse(kJson);
|
||||||
|
@ -2578,6 +2578,37 @@ TEST(SchemaValidator, Ref_internal_id_and_schema_pointer) {
|
|||||||
kValidateDefaultFlags, SchemaValidatorType, PointerType);
|
kValidateDefaultFlags, SchemaValidatorType, PointerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test that $refs are correctly resolved when intermediate multiple ids are present
|
||||||
|
// Includes $ref to a part of the document with a different in-scope id, which also contains $ref..
|
||||||
|
TEST(SchemaValidator, Ref_internal_multiple_ids) {
|
||||||
|
typedef GenericSchemaDocument<Value, MemoryPoolAllocator<> > SchemaDocumentType;
|
||||||
|
//RemoteSchemaDocumentProvider<SchemaDocumentType> provider;
|
||||||
|
CrtAllocator allocator;
|
||||||
|
char* schema = ReadFile("unittestschema/idandref.json", allocator);
|
||||||
|
Document sd;
|
||||||
|
sd.Parse(schema);
|
||||||
|
ASSERT_FALSE(sd.HasParseError());
|
||||||
|
SchemaDocumentType s(sd, "http://xyz", 10/*, &provider*/);
|
||||||
|
typedef GenericSchemaValidator<SchemaDocumentType, BaseReaderHandler<UTF8<> >, MemoryPoolAllocator<> > SchemaValidatorType;
|
||||||
|
typedef GenericPointer<Value, MemoryPoolAllocator<> > PointerType;
|
||||||
|
INVALIDATE_(s, "{\"PA1\": \"s\", \"PA2\": \"t\", \"PA3\": \"r\", \"PX1\": 1, \"PX2Y\": 2, \"PX3Z\": 3, \"PX4\": 4, \"PX5\": 5, \"PX6\": 6, \"PX7W\": 7, \"PX8N\": { \"NX\": 8}}", "#", "errors", "#",
|
||||||
|
"{ \"type\": ["
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PA1\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PA2\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PA3\", \"schemaRef\": \"http://xyz#/definitions/A\", \"expected\": [\"integer\"], \"actual\": \"string\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX1\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX2Y\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX3Z\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX4\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX5\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX6\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX7W\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"},"
|
||||||
|
" {\"errorCode\": 20, \"instanceRef\": \"#/PX8N/NX\", \"schemaRef\": \"http://xyz#/definitions/B/definitions/X\", \"expected\": [\"boolean\"], \"actual\": \"integer\"}"
|
||||||
|
"]}",
|
||||||
|
kValidateDefaultFlags | kValidateContinueOnErrorFlag, SchemaValidatorType, PointerType);
|
||||||
|
CrtAllocator::Free(schema);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(SchemaValidator, Ref_remote_issue1210) {
|
TEST(SchemaValidator, Ref_remote_issue1210) {
|
||||||
class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
|
class SchemaDocumentProvider : public IRemoteSchemaDocumentProvider {
|
||||||
SchemaDocument** collection;
|
SchemaDocument** collection;
|
||||||
@ -2916,250 +2947,6 @@ TEST(SchemaValidator, Schema_UnknownError) {
|
|||||||
ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null"));
|
ASSERT_TRUE(SchemaValidator::SchemaType::GetValidateErrorKeyword(kValidateErrors).GetString() == std::string("null"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(SchemaValidator, Uri_Parse) {
|
|
||||||
typedef std::basic_string<Value::Ch> String;
|
|
||||||
typedef Uri<GenericSchemaDocument<Value, MemoryPoolAllocator<> > > Uri;
|
|
||||||
MemoryPoolAllocator<CrtAllocator> allocator;
|
|
||||||
|
|
||||||
String s = "http://auth/path?query#frag";
|
|
||||||
Value v;
|
|
||||||
v.SetString(s, allocator);
|
|
||||||
Uri u = Uri(v);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "/path");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "http://auth/path?query");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "?query");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "#frag");
|
|
||||||
Value w;
|
|
||||||
u.Get(w, allocator);
|
|
||||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
|
||||||
|
|
||||||
s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f";
|
|
||||||
v.SetString(s, allocator);
|
|
||||||
u = Uri(v);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "urn:");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
u.Get(w, allocator);
|
|
||||||
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
|
||||||
|
|
||||||
s = "";
|
|
||||||
v.SetString(s, allocator);
|
|
||||||
u = Uri(v);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
|
|
||||||
s = "http://auth/";
|
|
||||||
v.SetString(s, allocator);
|
|
||||||
u = Uri(v);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "/");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "http://auth/");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
|
|
||||||
s = "/path/sub";
|
|
||||||
u = Uri(s);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "/path/sub");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "/path/sub");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
|
|
||||||
// absolute path gets normalized
|
|
||||||
s = "/path/../sub/";
|
|
||||||
u = Uri(s);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "/sub/");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "/sub/");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
|
|
||||||
// relative path does not
|
|
||||||
s = "path/../sub";
|
|
||||||
u = Uri(s);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "path/../sub");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "path/../sub");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "");
|
|
||||||
|
|
||||||
s = "http://auth#frag/stuff";
|
|
||||||
u = Uri(s);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "http:");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "//auth");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "http://auth");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
|
||||||
EXPECT_TRUE(u.Get() == s);
|
|
||||||
|
|
||||||
s = "#frag/stuff";
|
|
||||||
u = Uri(s);
|
|
||||||
EXPECT_TRUE(u.GetScheme() == "");
|
|
||||||
EXPECT_TRUE(u.GetAuth() == "");
|
|
||||||
EXPECT_TRUE(u.GetPath() == "");
|
|
||||||
EXPECT_TRUE(u.GetDoc() == "");
|
|
||||||
EXPECT_TRUE(u.GetQuery() == "");
|
|
||||||
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
|
||||||
EXPECT_TRUE(u.Get() == s);
|
|
||||||
|
|
||||||
Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
|
|
||||||
u = Uri(c, 11);
|
|
||||||
EXPECT_TRUE(String(u.GetString()) == "#frag/stuff");
|
|
||||||
EXPECT_TRUE(u.GetStringLength() == 11);
|
|
||||||
EXPECT_TRUE(String(u.GetDocString()) == "");
|
|
||||||
EXPECT_TRUE(u.GetDocStringLength() == 0);
|
|
||||||
EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff");
|
|
||||||
EXPECT_TRUE(u.GetFragStringLength() == 11);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(SchemaValidator, Uri_Resolve) {
|
|
||||||
typedef std::basic_string<Value::Ch> String;
|
|
||||||
typedef Uri<GenericSchemaDocument<Value, MemoryPoolAllocator<> > > Uri;
|
|
||||||
|
|
||||||
// ref is full uri
|
|
||||||
Uri base = Uri(String("http://auth/path/#frag"));
|
|
||||||
Uri ref = Uri(String("http://newauth/newpath#newfrag"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
|
||||||
|
|
||||||
base = Uri(String("/path/#frag"));
|
|
||||||
ref = Uri(String("http://newauth/newpath#newfrag"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
|
||||||
|
|
||||||
// ref is alternate uri
|
|
||||||
base = Uri(String("http://auth/path/#frag"));
|
|
||||||
ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
|
||||||
|
|
||||||
// ref is absolute path
|
|
||||||
base = Uri(String("http://auth/path/#"));
|
|
||||||
ref = Uri(String("/newpath#newfrag"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag");
|
|
||||||
|
|
||||||
// ref is relative path
|
|
||||||
base = Uri(String("http://auth/path/file.json#frag"));
|
|
||||||
ref = Uri(String("newfile.json#"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#");
|
|
||||||
|
|
||||||
base = Uri(String("http://auth/path/file.json#frag/stuff"));
|
|
||||||
ref = Uri(String("newfile.json#newfrag/newstuff"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff");
|
|
||||||
|
|
||||||
base = Uri(String("file.json"));
|
|
||||||
ref = Uri(String("newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
|
||||||
|
|
||||||
base = Uri(String("file.json"));
|
|
||||||
ref = Uri(String("./newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
|
||||||
|
|
||||||
base = Uri(String("file.json"));
|
|
||||||
ref = Uri(String("parent/../newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
|
||||||
|
|
||||||
base = Uri(String("file.json"));
|
|
||||||
ref = Uri(String("parent/./newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json");
|
|
||||||
|
|
||||||
base = Uri(String("file.json"));
|
|
||||||
ref = Uri(String("../../parent/.././newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
|
||||||
|
|
||||||
base = Uri(String("http://auth"));
|
|
||||||
ref = Uri(String("newfile.json"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json");
|
|
||||||
|
|
||||||
// ref is fragment
|
|
||||||
base = Uri(String("#frag/stuff"));
|
|
||||||
ref = Uri(String("#newfrag/newstuff"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff");
|
|
||||||
|
|
||||||
// test ref fragment always wins
|
|
||||||
base = Uri(String("/path#frag"));
|
|
||||||
ref = Uri(String(""));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "/path");
|
|
||||||
|
|
||||||
// Examples from RFC3896
|
|
||||||
base = Uri(String("http://a/b/c/d;p?q"));
|
|
||||||
ref = Uri(String("g:h"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "g:h");
|
|
||||||
ref = Uri(String("g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
|
||||||
ref = Uri(String("./g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
|
||||||
ref = Uri(String("g/"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/");
|
|
||||||
ref = Uri(String("/g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("//g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://g");
|
|
||||||
ref = Uri(String("?y"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y");
|
|
||||||
ref = Uri(String("g?y"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y");
|
|
||||||
ref = Uri(String("#s"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s");
|
|
||||||
ref = Uri(String("g#s"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s");
|
|
||||||
ref = Uri(String("g?y#s"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s");
|
|
||||||
ref = Uri(String(";x"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x");
|
|
||||||
ref = Uri(String("g;x"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x");
|
|
||||||
ref = Uri(String("g;x?y#s"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s");
|
|
||||||
ref = Uri(String(""));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q");
|
|
||||||
ref = Uri(String("."));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
|
||||||
ref = Uri(String("./"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
|
||||||
ref = Uri(String(".."));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
|
||||||
ref = Uri(String("../"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
|
||||||
ref = Uri(String("../g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g");
|
|
||||||
ref = Uri(String("../.."));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
|
||||||
ref = Uri(String("../../"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
|
||||||
ref = Uri(String("../../g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("../../../g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("../../../../g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("/./g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("/../g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
|
||||||
ref = Uri(String("g."));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.");
|
|
||||||
ref = Uri(String(".g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g");
|
|
||||||
ref = Uri(String("g.."));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g..");
|
|
||||||
ref = Uri(String("..g"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g");
|
|
||||||
ref = Uri(String("g#s/../x"));
|
|
||||||
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x");
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_MSC_VER) || defined(__clang__)
|
#if defined(_MSC_VER) || defined(__clang__)
|
||||||
RAPIDJSON_DIAG_POP
|
RAPIDJSON_DIAG_POP
|
||||||
#endif
|
#endif
|
||||||
|
278
test/unittest/uritest.cpp
Normal file
278
test/unittest/uritest.cpp
Normal file
@ -0,0 +1,278 @@
|
|||||||
|
// 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.
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
#define RAPIDJSON_SCHEMA_VERBOSE 0
|
||||||
|
#define RAPIDJSON_HAS_STDSTRING 1
|
||||||
|
|
||||||
|
#include "unittest.h"
|
||||||
|
#include "rapidjson/document.h"
|
||||||
|
#include "rapidjson/uri.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
RAPIDJSON_DIAG_PUSH
|
||||||
|
RAPIDJSON_DIAG_OFF(variadic-macros)
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
RAPIDJSON_DIAG_PUSH
|
||||||
|
RAPIDJSON_DIAG_OFF(4822) // local class member function does not have a body
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace rapidjson;
|
||||||
|
|
||||||
|
TEST(Uri, Parse) {
|
||||||
|
typedef std::basic_string<Value::Ch> String;
|
||||||
|
typedef GenericUri<Value, MemoryPoolAllocator<> > Uri;
|
||||||
|
MemoryPoolAllocator<CrtAllocator> allocator;
|
||||||
|
|
||||||
|
String s = "http://auth/path?query#frag";
|
||||||
|
Value v;
|
||||||
|
v.SetString(s, allocator);
|
||||||
|
Uri u = Uri(v);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "/path");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "http://auth/path?query");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "?query");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "#frag");
|
||||||
|
Value w;
|
||||||
|
u.Get(w, allocator);
|
||||||
|
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||||
|
|
||||||
|
s = "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f";
|
||||||
|
v.SetString(s, allocator);
|
||||||
|
u = Uri(v);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "urn:");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
u.Get(w, allocator);
|
||||||
|
EXPECT_TRUE(*w.GetString() == *v.GetString());
|
||||||
|
|
||||||
|
s = "";
|
||||||
|
v.SetString(s, allocator);
|
||||||
|
u = Uri(v);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
|
||||||
|
s = "http://auth/";
|
||||||
|
v.SetString(s, allocator);
|
||||||
|
u = Uri(v);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "/");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "http://auth/");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
|
||||||
|
s = "/path/sub";
|
||||||
|
u = Uri(s);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "/path/sub");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "/path/sub");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
|
||||||
|
// absolute path gets normalized
|
||||||
|
s = "/path/../sub/";
|
||||||
|
u = Uri(s);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "/sub/");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "/sub/");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
|
||||||
|
// relative path does not
|
||||||
|
s = "path/../sub";
|
||||||
|
u = Uri(s);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "path/../sub");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "path/../sub");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "");
|
||||||
|
|
||||||
|
s = "http://auth#frag/stuff";
|
||||||
|
u = Uri(s);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "http:");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "//auth");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "http://auth");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
||||||
|
EXPECT_TRUE(u.Get() == s);
|
||||||
|
|
||||||
|
s = "#frag/stuff";
|
||||||
|
u = Uri(s);
|
||||||
|
EXPECT_TRUE(u.GetScheme() == "");
|
||||||
|
EXPECT_TRUE(u.GetAuth() == "");
|
||||||
|
EXPECT_TRUE(u.GetPath() == "");
|
||||||
|
EXPECT_TRUE(u.GetBase() == "");
|
||||||
|
EXPECT_TRUE(u.GetQuery() == "");
|
||||||
|
EXPECT_TRUE(u.GetFrag() == "#frag/stuff");
|
||||||
|
EXPECT_TRUE(u.Get() == s);
|
||||||
|
|
||||||
|
Value::Ch c[] = { '#', 'f', 'r', 'a', 'g', '/', 's', 't', 'u', 'f', 'f', '\0'};
|
||||||
|
u = Uri(c, 11);
|
||||||
|
EXPECT_TRUE(String(u.GetString()) == "#frag/stuff");
|
||||||
|
EXPECT_TRUE(u.GetStringLength() == 11);
|
||||||
|
EXPECT_TRUE(String(u.GetBaseString()) == "");
|
||||||
|
EXPECT_TRUE(u.GetBaseStringLength() == 0);
|
||||||
|
EXPECT_TRUE(String(u.GetFragString()) == "#frag/stuff");
|
||||||
|
EXPECT_TRUE(u.GetFragStringLength() == 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Uri, Resolve) {
|
||||||
|
typedef std::basic_string<Value::Ch> String;
|
||||||
|
typedef GenericUri<Value, MemoryPoolAllocator<> > Uri;
|
||||||
|
|
||||||
|
// ref is full uri
|
||||||
|
Uri base = Uri(String("http://auth/path/#frag"));
|
||||||
|
Uri ref = Uri(String("http://newauth/newpath#newfrag"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
||||||
|
|
||||||
|
base = Uri(String("/path/#frag"));
|
||||||
|
ref = Uri(String("http://newauth/newpath#newfrag"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://newauth/newpath#newfrag");
|
||||||
|
|
||||||
|
// ref is alternate uri
|
||||||
|
base = Uri(String("http://auth/path/#frag"));
|
||||||
|
ref = Uri(String("urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "urn:uuid:ee564b8a-7a87-4125-8c96-e9f123d6766f");
|
||||||
|
|
||||||
|
// ref is absolute path
|
||||||
|
base = Uri(String("http://auth/path/#"));
|
||||||
|
ref = Uri(String("/newpath#newfrag"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newpath#newfrag");
|
||||||
|
|
||||||
|
// ref is relative path
|
||||||
|
base = Uri(String("http://auth/path/file.json#frag"));
|
||||||
|
ref = Uri(String("newfile.json#"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#");
|
||||||
|
|
||||||
|
base = Uri(String("http://auth/path/file.json#frag/stuff"));
|
||||||
|
ref = Uri(String("newfile.json#newfrag/newstuff"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/path/newfile.json#newfrag/newstuff");
|
||||||
|
|
||||||
|
base = Uri(String("file.json"));
|
||||||
|
ref = Uri(String("newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||||
|
|
||||||
|
base = Uri(String("file.json"));
|
||||||
|
ref = Uri(String("./newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||||
|
|
||||||
|
base = Uri(String("file.json"));
|
||||||
|
ref = Uri(String("parent/../newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||||
|
|
||||||
|
base = Uri(String("file.json"));
|
||||||
|
ref = Uri(String("parent/./newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "parent/newfile.json");
|
||||||
|
|
||||||
|
base = Uri(String("file.json"));
|
||||||
|
ref = Uri(String("../../parent/.././newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "newfile.json");
|
||||||
|
|
||||||
|
base = Uri(String("http://auth"));
|
||||||
|
ref = Uri(String("newfile.json"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://auth/newfile.json");
|
||||||
|
|
||||||
|
// ref is fragment
|
||||||
|
base = Uri(String("#frag/stuff"));
|
||||||
|
ref = Uri(String("#newfrag/newstuff"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "#newfrag/newstuff");
|
||||||
|
|
||||||
|
// test ref fragment always wins
|
||||||
|
base = Uri(String("/path#frag"));
|
||||||
|
ref = Uri(String(""));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "/path");
|
||||||
|
|
||||||
|
// Examples from RFC3896
|
||||||
|
base = Uri(String("http://a/b/c/d;p?q"));
|
||||||
|
ref = Uri(String("g:h"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "g:h");
|
||||||
|
ref = Uri(String("g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
||||||
|
ref = Uri(String("./g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g");
|
||||||
|
ref = Uri(String("g/"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g/");
|
||||||
|
ref = Uri(String("/g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("//g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://g");
|
||||||
|
ref = Uri(String("?y"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?y");
|
||||||
|
ref = Uri(String("g?y"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y");
|
||||||
|
ref = Uri(String("#s"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q#s");
|
||||||
|
ref = Uri(String("g#s"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s");
|
||||||
|
ref = Uri(String("g?y#s"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g?y#s");
|
||||||
|
ref = Uri(String(";x"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/;x");
|
||||||
|
ref = Uri(String("g;x"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x");
|
||||||
|
ref = Uri(String("g;x?y#s"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g;x?y#s");
|
||||||
|
ref = Uri(String(""));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/d;p?q");
|
||||||
|
ref = Uri(String("."));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
||||||
|
ref = Uri(String("./"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/");
|
||||||
|
ref = Uri(String(".."));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
||||||
|
ref = Uri(String("../"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/");
|
||||||
|
ref = Uri(String("../g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/g");
|
||||||
|
ref = Uri(String("../.."));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
||||||
|
ref = Uri(String("../../"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/");
|
||||||
|
ref = Uri(String("../../g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("../../../g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("../../../../g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("/./g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("/../g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/g");
|
||||||
|
ref = Uri(String("g."));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g.");
|
||||||
|
ref = Uri(String(".g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/.g");
|
||||||
|
ref = Uri(String("g.."));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g..");
|
||||||
|
ref = Uri(String("..g"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/..g");
|
||||||
|
ref = Uri(String("g#s/../x"));
|
||||||
|
EXPECT_TRUE(ref.Resolve(base).Get() == "http://a/b/c/g#s/../x");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__clang__)
|
||||||
|
RAPIDJSON_DIAG_POP
|
||||||
|
#endif
|
Loading…
x
Reference in New Issue
Block a user